Compare commits
1806 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04ed50fb0f | ||
|
|
c9553411bb | ||
|
|
62221adf55 | ||
|
|
a6bf4746c6 | ||
|
|
72637d00e8 | ||
|
|
27d4b15925 | ||
|
|
7a77767e66 | ||
|
|
1b26621860 | ||
|
|
3db5f4d0aa | ||
|
|
b2a6e327bf | ||
|
|
9aec5fe907 | ||
|
|
c731a82b71 | ||
|
|
e6fad97edc | ||
|
|
70495aada1 | ||
|
|
f243f615ef | ||
|
|
807c72b2f1 | ||
|
|
74dbaade6f | ||
|
|
53677172f2 | ||
|
|
bef0ccf42a | ||
|
|
ff35aa07dc | ||
|
|
a3afd5178c | ||
|
|
f1285a6dfd | ||
|
|
cf7c1afb93 | ||
|
|
e140a28073 | ||
|
|
de61c3b962 | ||
|
|
c46fc4531b | ||
|
|
065a9ed10f | ||
|
|
e44c0254f7 | ||
|
|
13f9f0315f | ||
|
|
1532ffe215 | ||
|
|
b24cbc68ba | ||
|
|
976fdd76c1 | ||
|
|
bbda335e02 | ||
|
|
d2dd6f7c70 | ||
|
|
e9a544fa98 | ||
|
|
79f2f8cddc | ||
|
|
39fa949345 | ||
|
|
e1d7a5cbf5 | ||
|
|
f3cefaf043 | ||
|
|
b3460f3f54 | ||
|
|
1e0ad1f6bf | ||
|
|
4abcb08cc9 | ||
|
|
81e7833711 | ||
|
|
82e261ad33 | ||
|
|
cae2811762 | ||
|
|
09112c6869 | ||
|
|
77aaf1baee | ||
|
|
6626c2d00d | ||
|
|
315f9d98f6 | ||
|
|
f087518e7a | ||
|
|
db9997a106 | ||
|
|
0cbfb610f2 | ||
|
|
f901cd042b | ||
|
|
5719207dfa | ||
|
|
a84fbbe327 | ||
|
|
93c97972b9 | ||
|
|
ac61d43720 | ||
|
|
1958f78cc1 | ||
|
|
a7ee64a25b | ||
|
|
2a58f22649 | ||
|
|
f066bb7716 | ||
|
|
4e3d182189 | ||
|
|
205fba74cf | ||
|
|
a9bcf8d50d | ||
|
|
b0ff3bc7a3 | ||
|
|
06301e279c | ||
|
|
6d6f274648 | ||
|
|
9acefcb256 | ||
|
|
e4d0293a31 | ||
|
|
ae77542a11 | ||
|
|
980187f856 | ||
|
|
327a8e6c59 | ||
|
|
d11e2724c4 | ||
|
|
f768ca3a2d | ||
|
|
947e902288 | ||
|
|
3436a646b5 | ||
|
|
aed0af1e00 | ||
|
|
0d7ee2b014 | ||
|
|
2f5fd91bd8 | ||
|
|
5761800197 | ||
|
|
0eebacc521 | ||
|
|
4a5528697d | ||
|
|
d38034bb5c | ||
|
|
b3fe3e8b3d | ||
|
|
5489dec28d | ||
|
|
8a6b4db19f | ||
|
|
df43b6a05c | ||
|
|
3adca3c2fa | ||
|
|
39abd3ecb4 | ||
|
|
a4f4fc50b9 | ||
|
|
7bf638b0de | ||
|
|
56662a703c | ||
|
|
8b5f47d3a3 | ||
|
|
a289dcb9ee | ||
|
|
023b9c1e7e | ||
|
|
4c61662644 | ||
|
|
ad1b6ef0ac | ||
|
|
ed5c6be2f1 | ||
|
|
30cb082932 | ||
|
|
645554d12f | ||
|
|
212b0faf0c | ||
|
|
276a0d9500 | ||
|
|
384b6c8288 | ||
|
|
a2af158fd3 | ||
|
|
f33d2fb2e7 | ||
|
|
a9eec35aff | ||
|
|
8534f2d1e2 | ||
|
|
00740fb23b | ||
|
|
37e9010887 | ||
|
|
4bd2c75056 | ||
|
|
b9ed850b98 | ||
|
|
11cea17496 | ||
|
|
163597ef69 | ||
|
|
95f84afd33 | ||
|
|
9f7fd1fbfb | ||
|
|
940cab8620 | ||
|
|
5b84c99d79 | ||
|
|
b295024574 | ||
|
|
34180f1745 | ||
|
|
665031467a | ||
|
|
df33171107 | ||
|
|
53f4044890 | ||
|
|
a5049136ff | ||
|
|
705b3c6b63 | ||
|
|
6b4ac66962 | ||
|
|
0964b06240 | ||
|
|
92eeca3ba7 | ||
|
|
2a86554ac4 | ||
|
|
805caa30ce | ||
|
|
a56949e9fa | ||
|
|
43da4c0eca | ||
|
|
b9c7771830 | ||
|
|
35db88affe | ||
|
|
e38faca455 | ||
|
|
0255e8710c | ||
|
|
6d89020f80 | ||
|
|
9c56c49e73 | ||
|
|
acb798e544 | ||
|
|
c5720a15c7 | ||
|
|
90709b332a | ||
|
|
81f17d10c8 | ||
|
|
773de38bd9 | ||
|
|
a48704925d | ||
|
|
fa4beeee75 | ||
|
|
d8351772d3 | ||
|
|
68d1abdb85 | ||
|
|
7e8474a85a | ||
|
|
82da364b8b | ||
|
|
7fa91ec175 | ||
|
|
1d3a09d377 | ||
|
|
02563a35f0 | ||
|
|
d653f35bb7 | ||
|
|
a543627abd | ||
|
|
80f2ba7fca | ||
|
|
32bca64920 | ||
|
|
7fa1a84ec3 | ||
|
|
ab4bb26a0a | ||
|
|
4b8d258cff | ||
|
|
3c29aa6271 | ||
|
|
51464b4317 | ||
|
|
2fec463542 | ||
|
|
1affc641c4 | ||
|
|
0cfd4fff62 | ||
|
|
8904127c10 | ||
|
|
c46f48abec | ||
|
|
4acbf7b90d | ||
|
|
cbc1a58e93 | ||
|
|
1b5f33a435 | ||
|
|
41b4a63f2b | ||
|
|
d8fc2db910 | ||
|
|
dc11dea7cc | ||
|
|
811af02f56 | ||
|
|
8780e23ed3 | ||
|
|
be492ed108 | ||
|
|
24da14f4f7 | ||
|
|
03d2fb450f | ||
|
|
67cba251c8 | ||
|
|
0bc511715b | ||
|
|
27ce80544f | ||
|
|
04f627c2af | ||
|
|
e72eef421b | ||
|
|
016063c810 | ||
|
|
38f19981b2 | ||
|
|
40dd968f13 | ||
|
|
3cef348f30 | ||
|
|
b293b16007 | ||
|
|
f5f43db2da | ||
|
|
029555d192 | ||
|
|
fa4d202e71 | ||
|
|
a8ebfd7a92 | ||
|
|
b19e5720cc | ||
|
|
a254f5a3a8 | ||
|
|
143c735f96 | ||
|
|
951bad46e0 | ||
|
|
716225cd2f | ||
|
|
bbc618b8f9 | ||
|
|
11ead56d6d | ||
|
|
e972ae4afa | ||
|
|
0709065f50 | ||
|
|
d6bc5c35a7 | ||
|
|
dc03f003ac | ||
|
|
7aa2104596 | ||
|
|
460cfba6ff | ||
|
|
c8b93d6573 | ||
|
|
3f5f96ac91 | ||
|
|
7c6b991de7 | ||
|
|
82460aa49f | ||
|
|
7e7b403043 | ||
|
|
e5217e6ce9 | ||
|
|
c98cb1d6f9 | ||
|
|
ba6f2b0467 | ||
|
|
23465ad985 | ||
|
|
7886a14b74 | ||
|
|
466b6a23cd | ||
|
|
4a04f73434 | ||
|
|
134cb6a017 | ||
|
|
8d036c4b7c | ||
|
|
c64ad78c7b | ||
|
|
4a043a915f | ||
|
|
8ff0d99092 | ||
|
|
2e47cb12c4 | ||
|
|
ff6f1655f0 | ||
|
|
b5ba94f1de | ||
|
|
cbf79769d3 | ||
|
|
125eb01e03 | ||
|
|
ccb13205f4 | ||
|
|
6f23e91e33 | ||
|
|
1bd8a322f5 | ||
|
|
362e73bea8 | ||
|
|
9f8c2b3b56 | ||
|
|
6a7f6cdacd | ||
|
|
5715342fe0 | ||
|
|
38a0d15190 | ||
|
|
56f763a4a8 | ||
|
|
a2eb14f3b3 | ||
|
|
05c63af7c4 | ||
|
|
1f59701c46 | ||
|
|
ec3191f502 | ||
|
|
32b5654a6e | ||
|
|
674091424e | ||
|
|
6ad336743d | ||
|
|
c882568ccd | ||
|
|
f6b2899dd2 | ||
|
|
bccd4ef2f7 | ||
|
|
94c240a026 | ||
|
|
c50a0cf7bf | ||
|
|
c37f7abb79 | ||
|
|
432ce9b1de | ||
|
|
054323c2bc | ||
|
|
a8770aa606 | ||
|
|
7d5442e103 | ||
|
|
eab32f2e5d | ||
|
|
d42c0f1dc5 | ||
|
|
6ad1e4d99a | ||
|
|
7350144ab3 | ||
|
|
54c591bd9d | ||
|
|
217d88f21f | ||
|
|
394e3be482 | ||
|
|
d7f024c510 | ||
|
|
bc5a53574c | ||
|
|
30df709736 | ||
|
|
8cd17ce045 | ||
|
|
1bfbced258 | ||
|
|
6ac5980a17 | ||
|
|
2e24adae89 | ||
|
|
188b94cb3e | ||
|
|
c48733e34f | ||
|
|
f36db9bb04 | ||
|
|
30dd29e251 | ||
|
|
6cf1acfb48 | ||
|
|
a7b09d3d1c | ||
|
|
8fc3768166 | ||
|
|
b07bddf742 | ||
|
|
220f957cd8 | ||
|
|
8ce48d83eb | ||
|
|
200cdb6b0a | ||
|
|
d9fb40203a | ||
|
|
2d9e972195 | ||
|
|
97a1a04116 | ||
|
|
493cd866f1 | ||
|
|
063d369672 | ||
|
|
a0fae8dacc | ||
|
|
bc840b69d5 | ||
|
|
85301853d6 | ||
|
|
7cd53fb452 | ||
|
|
538ddf7af2 | ||
|
|
d5afa181f7 | ||
|
|
8ed4124184 | ||
|
|
160242a74f | ||
|
|
81b83bc904 | ||
|
|
2a716b7a7a | ||
|
|
63272541eb | ||
|
|
464767c5fd | ||
|
|
a9c3ca8606 | ||
|
|
86dd677e0c | ||
|
|
666f700a93 | ||
|
|
b70bf938c2 | ||
|
|
d5cf41e043 | ||
|
|
b7a1954c33 | ||
|
|
6520589a37 | ||
|
|
f445b0178a | ||
|
|
f618065f7c | ||
|
|
f0be48ff90 | ||
|
|
bb922d577d | ||
|
|
990809cc21 | ||
|
|
bfcc466647 | ||
|
|
3d2558bde6 | ||
|
|
1aac0b10c9 | ||
|
|
e5ff85b63c | ||
|
|
f0bb5b84f9 | ||
|
|
07b50f6c69 | ||
|
|
61b3aaaa07 | ||
|
|
b5119d7958 | ||
|
|
d3e72e4344 | ||
|
|
60512ffa09 | ||
|
|
b1a252a64d | ||
|
|
fb90b64bac | ||
|
|
6e7a398263 | ||
|
|
44d2d9b1e8 | ||
|
|
8302ed44aa | ||
|
|
7e12aea1d8 | ||
|
|
20346b0da4 | ||
|
|
e0e65cbdf9 | ||
|
|
9d2d58c4b6 | ||
|
|
0c461c3859 | ||
|
|
0d38bd9b3b | ||
|
|
2f02e49b9f | ||
|
|
064e8a7c68 | ||
|
|
e4dd269609 | ||
|
|
19dd59f38c | ||
|
|
37501d9bc7 | ||
|
|
89f9561d10 | ||
|
|
8af3f91e78 | ||
|
|
0661fd6f7c | ||
|
|
edd003b62a | ||
|
|
10cc87e422 | ||
|
|
0470f648c6 | ||
|
|
777360149d | ||
|
|
aa7774b82b | ||
|
|
d44da875e7 | ||
|
|
f9ad73598b | ||
|
|
952fe98796 | ||
|
|
579912e52f | ||
|
|
1b5ec3e3ca | ||
|
|
fb4e6ac923 | ||
|
|
3560dc4be6 | ||
|
|
b002ea9a20 | ||
|
|
540317ea2b | ||
|
|
142d1951d2 | ||
|
|
cf06ba6d13 | ||
|
|
a8d800572e | ||
|
|
5525ea45a4 | ||
|
|
3120958a17 | ||
|
|
052726ed50 | ||
|
|
8397196fbb | ||
|
|
8547611479 | ||
|
|
fe45f28204 | ||
|
|
861067412f | ||
|
|
7eca886608 | ||
|
|
79b6f9e89e | ||
|
|
3d17c06777 | ||
|
|
d6c08fb79f | ||
|
|
ef02b20811 | ||
|
|
8bf46a665e | ||
|
|
c4fca2aa61 | ||
|
|
45310d0cf6 | ||
|
|
87268c2297 | ||
|
|
032e435490 | ||
|
|
78c43edcac | ||
|
|
f32d34b965 | ||
|
|
8a7ff6a6fd | ||
|
|
6c48aa5fae | ||
|
|
d82547acd3 | ||
|
|
441f9cc2ee | ||
|
|
9f4fc8ad33 | ||
|
|
d1e31261fe | ||
|
|
05dd9acba8 | ||
|
|
9270485723 | ||
|
|
e93975cb46 | ||
|
|
cbdaf4827f | ||
|
|
f2c28d287a | ||
|
|
90c228abca | ||
|
|
505e6bec9e | ||
|
|
ee027f237b | ||
|
|
3ae0d6f421 | ||
|
|
13b66a77c7 | ||
|
|
ed70836057 | ||
|
|
a68e52c2e3 | ||
|
|
ea37b89753 | ||
|
|
5d89aa06f7 | ||
|
|
ead034e638 | ||
|
|
a294838bcd | ||
|
|
1c03d1e87d | ||
|
|
f8804c8a56 | ||
|
|
7c444dea6e | ||
|
|
6d91d270d6 | ||
|
|
dd1cec4196 | ||
|
|
d1a8a4481e | ||
|
|
5921ffaa36 | ||
|
|
cf47b68c1e | ||
|
|
eeb8d0dbcd | ||
|
|
b6fa22bd84 | ||
|
|
a0ef27a0cd | ||
|
|
22eb831a71 | ||
|
|
01ab9acd14 | ||
|
|
74fcbb382f | ||
|
|
9836b1dddd | ||
|
|
e304d0f8ee | ||
|
|
ab7b38d4b9 | ||
|
|
a464dc681a | ||
|
|
eaf675dc92 | ||
|
|
57068e526c | ||
|
|
c14a00eec9 | ||
|
|
219c42522f | ||
|
|
e3a0f15837 | ||
|
|
a6bb27483b | ||
|
|
7ada7def9e | ||
|
|
af384d9aa6 | ||
|
|
223c129b6b | ||
|
|
421c4ae907 | ||
|
|
4907f610d6 | ||
|
|
f9d1bbbffb | ||
|
|
60589fc1cb | ||
|
|
5121316036 | ||
|
|
68bdfa9d0e | ||
|
|
97b816200d | ||
|
|
059a643188 | ||
|
|
7facad41b7 | ||
|
|
3ee59e454c | ||
|
|
43da1686da | ||
|
|
6d643f92b7 | ||
|
|
559a01f585 | ||
|
|
ba8e3f11e2 | ||
|
|
fd8a53ca3d | ||
|
|
8707aafaf7 | ||
|
|
e5a28bfd8d | ||
|
|
6e04d66a35 | ||
|
|
26bef5d209 | ||
|
|
cd6e0ff88a | ||
|
|
2ca979425f | ||
|
|
8d822ebdb4 | ||
|
|
bc5b5afcbf | ||
|
|
b373c53ce4 | ||
|
|
bc4b89c21a | ||
|
|
e10c287c93 | ||
|
|
ce42d53a09 | ||
|
|
a4f9d6d98b | ||
|
|
eebd03701a | ||
|
|
d5287682d1 | ||
|
|
7c567e3cbd | ||
|
|
58352ea69d | ||
|
|
de64b35359 | ||
|
|
7df8e1eab2 | ||
|
|
3158955198 | ||
|
|
d9d97bd17b | ||
|
|
94f6380d69 | ||
|
|
2ee821656f | ||
|
|
7556abb92d | ||
|
|
9d3a85d434 | ||
|
|
147d301f10 | ||
|
|
75a89c5983 | ||
|
|
2289968634 | ||
|
|
ca252804c6 | ||
|
|
7c25d83f1c | ||
|
|
8ce30c6a69 | ||
|
|
9fcaff749f | ||
|
|
3ac1475262 | ||
|
|
ad64bab5b2 | ||
|
|
f02998b106 | ||
|
|
127fe6ecf0 | ||
|
|
888ab0c89c | ||
|
|
2efd8ef52d | ||
|
|
6ad933982f | ||
|
|
854258f376 | ||
|
|
26f2d7fbae | ||
|
|
3952920492 | ||
|
|
c2001a7259 | ||
|
|
78f911ac19 | ||
|
|
0f2a7226fb | ||
|
|
f8d0ebe92f | ||
|
|
ba6ba7d4be | ||
|
|
58e6f660f3 | ||
|
|
cd482ca655 | ||
|
|
e43b56eb38 | ||
|
|
0c2d767f6f | ||
|
|
02e697032f | ||
|
|
07dc262690 | ||
|
|
6bf8d5b936 | ||
|
|
93858bf262 | ||
|
|
f64da46a98 | ||
|
|
2052a029ee | ||
|
|
c6aafff701 | ||
|
|
26f0f92210 | ||
|
|
bdc4ae2b86 | ||
|
|
9ca9341384 | ||
|
|
7f4f8b7c7d | ||
|
|
8870526457 | ||
|
|
f057e1e17a | ||
|
|
8561c9c5a6 | ||
|
|
23199719d9 | ||
|
|
a606ef0700 | ||
|
|
a234de1ee3 | ||
|
|
d266898617 | ||
|
|
69b5929c5a | ||
|
|
316c72a4ae | ||
|
|
9da88eec3e | ||
|
|
f10d9996d2 | ||
|
|
58ec9d3a73 | ||
|
|
4a503ba1ad | ||
|
|
2d096a569a | ||
|
|
e44ea5038e | ||
|
|
b111aa0111 | ||
|
|
773d24ebf7 | ||
|
|
bd371af0b3 | ||
|
|
9d79c72c17 | ||
|
|
a86aaef4d0 | ||
|
|
e463244db3 | ||
|
|
9e10b75f55 | ||
|
|
294091ce60 | ||
|
|
da6dd2dc92 | ||
|
|
40b9de66c7 | ||
|
|
aac985951a | ||
|
|
662cc5fe20 | ||
|
|
e1e62d97a8 | ||
|
|
a29cc48fec | ||
|
|
42af040fbd | ||
|
|
d383d617c2 | ||
|
|
9d04c21cc2 | ||
|
|
ed8039e223 | ||
|
|
f05743bf57 | ||
|
|
c6be661bb5 | ||
|
|
c84508ae9f | ||
|
|
77f2cd6513 | ||
|
|
baad86bcd7 | ||
|
|
8808aad529 | ||
|
|
6d7eaba845 | ||
|
|
4259b17b91 | ||
|
|
828ea700e8 | ||
|
|
7f22685fa3 | ||
|
|
40db9dff3b | ||
|
|
fa82264604 | ||
|
|
ae467aa42e | ||
|
|
67cc09416f | ||
|
|
f2a75fbfc7 | ||
|
|
b1fb09e183 | ||
|
|
cd0082c630 | ||
|
|
f78527d1e3 | ||
|
|
1f642238a7 | ||
|
|
6fe06cad98 | ||
|
|
2335fdbb5a | ||
|
|
1b6f7c3eb7 | ||
|
|
67f0d26d17 | ||
|
|
8574bcd494 | ||
|
|
02cc77cd82 | ||
|
|
d8782ce5fd | ||
|
|
dedc2986c6 | ||
|
|
74cdc0005a | ||
|
|
a756cd9565 | ||
|
|
ae27c3f4c5 | ||
|
|
6d41f36266 | ||
|
|
57cbcdf2ec | ||
|
|
455fd180b1 | ||
|
|
e6177c3349 | ||
|
|
e2adb82e29 | ||
|
|
bb472206de | ||
|
|
d87cf5146e | ||
|
|
87eb5cbced | ||
|
|
181edf4b53 | ||
|
|
dfa53cb88e | ||
|
|
d079cda174 | ||
|
|
4265e71d6f | ||
|
|
f66a72c66b | ||
|
|
9be90bd1c9 | ||
|
|
106e535577 | ||
|
|
194be51f05 | ||
|
|
a862e363ea | ||
|
|
2e64afca27 | ||
|
|
51cda0be2a | ||
|
|
4da3291157 | ||
|
|
52edabf2cb | ||
|
|
43a1a0f3ab | ||
|
|
e88524f274 | ||
|
|
7a929fcd27 | ||
|
|
7087fdf6c0 | ||
|
|
67093a5143 | ||
|
|
3f34016888 | ||
|
|
1307633a84 | ||
|
|
1a53f07d80 | ||
|
|
672f678ed6 | ||
|
|
b924568f79 | ||
|
|
e6e9c21275 | ||
|
|
636f5d4a1d | ||
|
|
41a4b280ee | ||
|
|
27139bf41f | ||
|
|
12ba1957d0 | ||
|
|
9b9d189a33 | ||
|
|
7c3af4f56f | ||
|
|
880ce080b7 | ||
|
|
6e84a03a35 | ||
|
|
a9f6556454 | ||
|
|
2722211ba3 | ||
|
|
e2e5cddcaa | ||
|
|
8b7718fbdc | ||
|
|
b44957ad91 | ||
|
|
a049f972ec | ||
|
|
069895d26b | ||
|
|
0e756e4377 | ||
|
|
ee7282ce0d | ||
|
|
d026367334 | ||
|
|
7244dc4511 | ||
|
|
56f61a6d59 | ||
|
|
4d5f610029 | ||
|
|
c94b4466d5 | ||
|
|
61f9e79ec9 | ||
|
|
cf797657ed | ||
|
|
c597538b40 | ||
|
|
43f964e28d | ||
|
|
8283f23651 | ||
|
|
7cc25f91ff | ||
|
|
5d2506e697 | ||
|
|
14908b7f28 | ||
|
|
2e122e1509 | ||
|
|
486b5b6bfc | ||
|
|
88a2f128ec | ||
|
|
45c139cfb3 | ||
|
|
3a111e5d74 | ||
|
|
fc0c5e7359 | ||
|
|
2ccfb71d12 | ||
|
|
e8a5a9b1e8 | ||
|
|
ec3568bd97 | ||
|
|
a67a881715 | ||
|
|
7a048f004d | ||
|
|
330b6a0482 | ||
|
|
aa2e4d92e0 | ||
|
|
0ea66a1275 | ||
|
|
aafff8fd5c | ||
|
|
5ac2a69940 | ||
|
|
8278a12af9 | ||
|
|
e88dd9b08b | ||
|
|
bbea6564fc | ||
|
|
1ca8d7ad45 | ||
|
|
67958f7fa7 | ||
|
|
ab9c527274 | ||
|
|
36ff991960 | ||
|
|
59d38f876a | ||
|
|
5c5c6a965c | ||
|
|
74aafe6a10 | ||
|
|
f860a2fbd6 | ||
|
|
87bcf739ee | ||
|
|
db6db51742 | ||
|
|
981be7956b | ||
|
|
8c10aa575c | ||
|
|
17b316b94b | ||
|
|
8921b4f9d1 | ||
|
|
cf16d81fa4 | ||
|
|
2beb763b4f | ||
|
|
69ae879c58 | ||
|
|
08fee9a284 | ||
|
|
f89da17827 | ||
|
|
ea26da0be7 | ||
|
|
1f3d3970f6 | ||
|
|
c9a71a7176 | ||
|
|
7ed8833fd5 | ||
|
|
9fb351a139 | ||
|
|
0d0ccacdf3 | ||
|
|
c8858f85d8 | ||
|
|
d1bc46ffad | ||
|
|
32e5848f34 | ||
|
|
be36c0769a | ||
|
|
f9e63dfd65 | ||
|
|
fa05dac5ae | ||
|
|
430dd3ae05 | ||
|
|
664fc76ac7 | ||
|
|
37d6cc07a7 | ||
|
|
d79b6cd8dc | ||
|
|
d6510f9578 | ||
|
|
59e8302c9d | ||
|
|
3422bd9b94 | ||
|
|
82ecebb393 | ||
|
|
6ba0b029e6 | ||
|
|
604c9dacdb | ||
|
|
1a619522ee | ||
|
|
88615fbff1 | ||
|
|
d4f8f67173 | ||
|
|
174a0a2efc | ||
|
|
12b5494906 | ||
|
|
aa0f06d6b7 | ||
|
|
0c47685e02 | ||
|
|
e0ca4347be | ||
|
|
e65d9408ee | ||
|
|
d8558a3af3 | ||
|
|
72eedb1c98 | ||
|
|
3c5cf9500d | ||
|
|
7a1f3177c9 | ||
|
|
f8da8b0261 | ||
|
|
393cb7fd7d | ||
|
|
e74e1256d4 | ||
|
|
96abd70c13 | ||
|
|
1f9d9c3176 | ||
|
|
74e95e88d8 | ||
|
|
80d9baa9e8 | ||
|
|
751995ab95 | ||
|
|
5ca6e2910a | ||
|
|
071d05465a | ||
|
|
11a9536271 | ||
|
|
41a7203c28 | ||
|
|
543a58bb87 | ||
|
|
b2b95cad20 | ||
|
|
eabec967ec | ||
|
|
e42b152037 | ||
|
|
da599e3f1a | ||
|
|
4c7b0b935b | ||
|
|
c400876df1 | ||
|
|
c4bea3dfe4 | ||
|
|
eaa9a1e33b | ||
|
|
3e19298c9e | ||
|
|
2a96ce97ee | ||
|
|
8cfe901391 | ||
|
|
30f1ee7a1f | ||
|
|
46ff830daa | ||
|
|
828cd6fd0b | ||
|
|
681643ea9e | ||
|
|
fa947e02ca | ||
|
|
cd289843f7 | ||
|
|
b123392592 | ||
|
|
ccbb5c3e01 | ||
|
|
49b63e084f | ||
|
|
107321e385 | ||
|
|
1d214b4aed | ||
|
|
2e1347aba4 | ||
|
|
9ddb5931fb | ||
|
|
913064d6cc | ||
|
|
fb45b8a5c9 | ||
|
|
f5c9056430 | ||
|
|
0e39b59700 | ||
|
|
552af5455e | ||
|
|
8759bc1b41 | ||
|
|
56504534d8 | ||
|
|
80a45c91f9 | ||
|
|
3f695fdfaf | ||
|
|
88433bca22 | ||
|
|
34689eb2d2 | ||
|
|
211aea1441 | ||
|
|
74a46788cd | ||
|
|
7213c26798 | ||
|
|
fa1fb47d75 | ||
|
|
b9eeb6e6eb | ||
|
|
d5fa2af353 | ||
|
|
a877926f08 | ||
|
|
3e4e6f7ced | ||
|
|
936eb43c0e | ||
|
|
bb66cfa780 | ||
|
|
1d626cb6ac | ||
|
|
580346f4a7 | ||
|
|
0fbcf64108 | ||
|
|
267faa3851 | ||
|
|
fe50481f7b | ||
|
|
759616abd4 | ||
|
|
eb8fd07900 | ||
|
|
c99559dbe9 | ||
|
|
966c4244cb | ||
|
|
81283f8bcb | ||
|
|
8b84e5b3f9 | ||
|
|
e1d5ddb478 | ||
|
|
2da0322329 | ||
|
|
fefb35c782 | ||
|
|
b2433a664c | ||
|
|
adad4279f3 | ||
|
|
3ca0a39a35 | ||
|
|
52594e64d0 | ||
|
|
3364c1b893 | ||
|
|
0801b3f495 | ||
|
|
657f00fe36 | ||
|
|
044c81b644 | ||
|
|
507d8fdc6f | ||
|
|
31e29e62f4 | ||
|
|
dfcb572985 | ||
|
|
1d324176d1 | ||
|
|
37b3190752 | ||
|
|
d51265d48f | ||
|
|
dc14ac7a8f | ||
|
|
fb52a24da3 | ||
|
|
fbafb19657 | ||
|
|
6c4438d8a9 | ||
|
|
be00737ced | ||
|
|
07b93dcf80 | ||
|
|
374c6a27db | ||
|
|
36c9e95969 | ||
|
|
a3142ee4e6 | ||
|
|
437caeb90f | ||
|
|
af49cace8d | ||
|
|
13b78d0d89 | ||
|
|
65c135b451 | ||
|
|
ec162f265f | ||
|
|
ba99696771 | ||
|
|
4eeea640f4 | ||
|
|
cc6f1020d0 | ||
|
|
316ddc2382 | ||
|
|
3452625fac | ||
|
|
426e0105a6 | ||
|
|
18f39aa012 | ||
|
|
631b635652 | ||
|
|
49695d47d3 | ||
|
|
5cec477131 | ||
|
|
a5d0300787 | ||
|
|
09b84c51ee | ||
|
|
afe53997bf | ||
|
|
c42e412c0a | ||
|
|
542e7feba8 | ||
|
|
dc085e0bd2 | ||
|
|
7c9517343c | ||
|
|
0b4fa41aff | ||
|
|
e138e2c880 | ||
|
|
0d6345e98b | ||
|
|
a84bcf597d | ||
|
|
4bbd4ba855 | ||
|
|
9e5b56b475 | ||
|
|
dd383037fd | ||
|
|
3cd24c0a51 | ||
|
|
41507d8129 | ||
|
|
317a98a5a9 | ||
|
|
1395794923 | ||
|
|
073e9d06ec | ||
|
|
fd1b04932a | ||
|
|
d4b625b48e | ||
|
|
82337dec44 | ||
|
|
f1d0700252 | ||
|
|
e1383a2d8e | ||
|
|
6773adc771 | ||
|
|
ee2afb35dd | ||
|
|
0adcda387a | ||
|
|
fc1664d9fc | ||
|
|
12768babdf | ||
|
|
bc2a1160b8 | ||
|
|
75542e8f5d | ||
|
|
fcb55f841a | ||
|
|
154e601f4e | ||
|
|
4cca75b2e1 | ||
|
|
6d616e55ae | ||
|
|
95b1a640b6 | ||
|
|
ecc12c9ba1 | ||
|
|
9e50993c13 | ||
|
|
b32ec857b1 | ||
|
|
0de42d56fc | ||
|
|
6378ad69bb | ||
|
|
9616e9bf3b | ||
|
|
7c8b73bffb | ||
|
|
ae178c77bd | ||
|
|
2220651253 | ||
|
|
7fdd801479 | ||
|
|
27002ad1ea | ||
|
|
986dd2fac1 | ||
|
|
3965b490a9 | ||
|
|
b999e16406 | ||
|
|
7fb9bebd46 | ||
|
|
3b8a9dd6ec | ||
|
|
cfbee59152 | ||
|
|
b4db28fed2 | ||
|
|
c9aec3e866 | ||
|
|
1d1f8be260 | ||
|
|
38e6c0ad3f | ||
|
|
7ecf4ad54d | ||
|
|
1c02b4b9f4 | ||
|
|
afdefefbe4 | ||
|
|
5360c0c588 | ||
|
|
74a05a7f53 | ||
|
|
a68acf5c66 | ||
|
|
707d379b97 | ||
|
|
88a0a48b03 | ||
|
|
d51b90a880 | ||
|
|
e9912bcf8a | ||
|
|
4a5cc9f610 | ||
|
|
e29fc62b68 | ||
|
|
b55e1dcb5e | ||
|
|
6f4775a8ee | ||
|
|
c7e2f558a0 | ||
|
|
328860c8ba | ||
|
|
89ac111853 | ||
|
|
3d4f588a7f | ||
|
|
8f74bf314d | ||
|
|
0dd5ebbdbe | ||
|
|
2ae60767a3 | ||
|
|
64f31f70f9 | ||
|
|
23eacbd132 | ||
|
|
e9f16fca96 | ||
|
|
3d30de91f3 | ||
|
|
f68d2f7c7f | ||
|
|
f660d30138 | ||
|
|
b10276ff98 | ||
|
|
7d353bbe2a | ||
|
|
5696f91a1e | ||
|
|
3932e62fc7 | ||
|
|
729304aef5 | ||
|
|
40cce050e7 | ||
|
|
bb6ee71f08 | ||
|
|
0d0642fd67 | ||
|
|
08e6cf3dd2 | ||
|
|
044134eba0 | ||
|
|
9fa6fa522e | ||
|
|
d0cd98a63a | ||
|
|
88eae9dabb | ||
|
|
f2f1801c25 | ||
|
|
9da57e7458 | ||
|
|
3a3fb98f79 | ||
|
|
503ed9c331 | ||
|
|
cb63189f6b | ||
|
|
d77c83f4ba | ||
|
|
8cf3ac200b | ||
|
|
477877406a | ||
|
|
2bf2f34b12 | ||
|
|
7453c26ec4 | ||
|
|
b059ba69d6 | ||
|
|
36ca57a54e | ||
|
|
efa6678bcc | ||
|
|
b8f1850bba | ||
|
|
5268f55344 | ||
|
|
e44c9a000d | ||
|
|
ffc926bda5 | ||
|
|
07c1ba1f5e | ||
|
|
14168eadb2 | ||
|
|
58771fc41c | ||
|
|
1e06c66c77 | ||
|
|
c73771e3ce | ||
|
|
350d2bcd6e | ||
|
|
b2c523e56d | ||
|
|
5bc5abf0e7 | ||
|
|
44352e9ee4 | ||
|
|
7ccc609da2 | ||
|
|
ebc0168072 | ||
|
|
4ba7427fa0 | ||
|
|
9dc5335e3e | ||
|
|
d333de1980 | ||
|
|
96afa8bd2b | ||
|
|
797bbeabeb | ||
|
|
20cdab5546 | ||
|
|
6765234b60 | ||
|
|
4c27898ce7 | ||
|
|
e9a85aa4e4 | ||
|
|
2a16fc74fd | ||
|
|
f65254680a | ||
|
|
d64edb6896 | ||
|
|
3f9ad8e104 | ||
|
|
1c772ef699 | ||
|
|
809b89b5af | ||
|
|
681e012fb5 | ||
|
|
3be36643a1 | ||
|
|
21e19ef69f | ||
|
|
69a42fc901 | ||
|
|
525789cd36 | ||
|
|
367d660c08 | ||
|
|
4d73e4d605 | ||
|
|
d86cc5bf42 | ||
|
|
5f262da09d | ||
|
|
82985a9488 | ||
|
|
afcdb65f80 | ||
|
|
6b66e86f40 | ||
|
|
cb145d290e | ||
|
|
167803d2a1 | ||
|
|
58a981d2f6 | ||
|
|
8963cd1fab | ||
|
|
55cd5a9a78 | ||
|
|
3f4c283203 | ||
|
|
1e63e7c08b | ||
|
|
33fc3af775 | ||
|
|
6d9739165e | ||
|
|
7c5396ffb9 | ||
|
|
8319398957 | ||
|
|
a1509876de | ||
|
|
0f2cf51f43 | ||
|
|
e99ff4fdbc | ||
|
|
25431f32a2 | ||
|
|
6ba1fe7568 | ||
|
|
bfae92e307 | ||
|
|
9c5bf542d1 | ||
|
|
61161269d8 | ||
|
|
8fe004e98e | ||
|
|
09e5f17b5d | ||
|
|
db20e29af6 | ||
|
|
5f5c95cc68 | ||
|
|
68f3015712 | ||
|
|
240a697f6c | ||
|
|
5a0dc808fd | ||
|
|
befbf6bf13 | ||
|
|
2d5c21c457 | ||
|
|
25977d56c8 | ||
|
|
4dcf0b8ae0 | ||
|
|
9574d11dc8 | ||
|
|
3d4689756c | ||
|
|
90114514a9 | ||
|
|
7757e59e78 | ||
|
|
adb89e0121 | ||
|
|
b7a7953757 | ||
|
|
d25ef7eea4 | ||
|
|
30a82076ba | ||
|
|
860339c132 | ||
|
|
008723c62f | ||
|
|
9e0ce23a03 | ||
|
|
04ba433ca6 | ||
|
|
d884272ba8 | ||
|
|
e609c88334 | ||
|
|
e92a41fa3a | ||
|
|
df5f9f4a15 | ||
|
|
d9466cad0e | ||
|
|
150443b014 | ||
|
|
9e36af7916 | ||
|
|
85b8675e7a | ||
|
|
ae594ad92c | ||
|
|
8d6fedf817 | ||
|
|
f4f8fa7c94 | ||
|
|
fb5fbb8088 | ||
|
|
e06a807d30 | ||
|
|
6b09842cb6 | ||
|
|
ca813389dc | ||
|
|
e624171ba1 | ||
|
|
0bbfb28992 | ||
|
|
ee7bd695fd | ||
|
|
1cc17bfe7a | ||
|
|
8ae492dfeb | ||
|
|
e8789d7cb9 | ||
|
|
0053cd0d0d | ||
|
|
c32477a223 | ||
|
|
a0c25941a8 | ||
|
|
5eb468bce0 | ||
|
|
7f8dd0e939 | ||
|
|
28701cb9dd | ||
|
|
9b7f492c65 | ||
|
|
6eda79d02d | ||
|
|
fb5cf8e3ac | ||
|
|
55e128cced | ||
|
|
13056af8b2 | ||
|
|
972c52891d | ||
|
|
e10a8d95f6 | ||
|
|
97f2be348c | ||
|
|
2b21312b36 | ||
|
|
3a818b6d45 | ||
|
|
1709ab6810 | ||
|
|
ce925ba56f | ||
|
|
4ad14f6a2c | ||
|
|
579e48edbb | ||
|
|
6a08f2281a | ||
|
|
91729437a0 | ||
|
|
3af7af0b8f | ||
|
|
1ddd00433e | ||
|
|
de160bb2d1 | ||
|
|
ff62b6742b | ||
|
|
6da0af94df | ||
|
|
df9667a497 | ||
|
|
e3e2ad4ae5 | ||
|
|
ace4ba3171 | ||
|
|
0935ae330a | ||
|
|
9e02b13ab3 | ||
|
|
5a486a940c | ||
|
|
d05ae4b444 | ||
|
|
73b0610d89 | ||
|
|
73aec9ce63 | ||
|
|
a9fefcf600 | ||
|
|
59ce67e2e5 | ||
|
|
2182209a8a | ||
|
|
e29a8b3a9c | ||
|
|
ace3f37af4 | ||
|
|
37b6899660 | ||
|
|
570b12ec13 | ||
|
|
ac286ef734 | ||
|
|
b3663b5da2 | ||
|
|
d761d8b168 | ||
|
|
fe7c6fee34 | ||
|
|
9906daeca7 | ||
|
|
5fb54095d2 | ||
|
|
3fdc678aae | ||
|
|
a0088ccce1 | ||
|
|
be081929f4 | ||
|
|
b0b086d473 | ||
|
|
6798af52b6 | ||
|
|
4d5ebafa6d | ||
|
|
ec8873b178 | ||
|
|
c45f205593 | ||
|
|
027b2063ba | ||
|
|
b531c4c2fe | ||
|
|
4b010df99e | ||
|
|
5e4dd4be9c | ||
|
|
04d5896c1c | ||
|
|
f947274626 | ||
|
|
f6939f71a7 | ||
|
|
25aa17ad89 | ||
|
|
05ee057db5 | ||
|
|
297e2747f3 | ||
|
|
77de233117 | ||
|
|
02b67edaf5 | ||
|
|
8549ccfd8c | ||
|
|
ffd16b55a6 | ||
|
|
d744c997d8 | ||
|
|
6b421cc354 | ||
|
|
37ec29b225 | ||
|
|
ca7f6a26b5 | ||
|
|
9b69d22d7e | ||
|
|
4ab4cf8532 | ||
|
|
3341b282b7 | ||
|
|
bb3b836dbb | ||
|
|
1873c67540 | ||
|
|
8fd8f23a6b | ||
|
|
f225051348 | ||
|
|
b2e3fdef0f | ||
|
|
c13810ebaa | ||
|
|
19a982cf69 | ||
|
|
746a47982b | ||
|
|
327cbf48f5 | ||
|
|
0f1f509be7 | ||
|
|
c8f0c7e9ed | ||
|
|
a7989077ab | ||
|
|
7e34737b2b | ||
|
|
0a0659d737 | ||
|
|
b6df4680df | ||
|
|
e04090b477 | ||
|
|
c9fb6f7bdb | ||
|
|
ad6b05ae17 | ||
|
|
29b18d9ab7 | ||
|
|
ad309cdeae | ||
|
|
e4322a716c | ||
|
|
90886c0760 | ||
|
|
957beeb0e9 | ||
|
|
522ad4cca6 | ||
|
|
dc76b24e5f | ||
|
|
e3e3053f32 | ||
|
|
c8c3f20840 | ||
|
|
f6682aae77 | ||
|
|
0ba1b73395 | ||
|
|
4c995eb498 | ||
|
|
144cfe70bf | ||
|
|
855f26c43d | ||
|
|
f86e159536 | ||
|
|
243c4e1e83 | ||
|
|
9ae7f186bc | ||
|
|
f8d114be42 | ||
|
|
2c70251df1 | ||
|
|
d380db25be | ||
|
|
e8938b1069 | ||
|
|
4a28333039 | ||
|
|
ad7f2b2aec | ||
|
|
ff665b37cb | ||
|
|
ce73843014 | ||
|
|
bae6f653d1 | ||
|
|
eec6d09959 | ||
|
|
c5fa8ed321 | ||
|
|
abc16b919d | ||
|
|
2de7cd32ea | ||
|
|
098aa18538 | ||
|
|
cf6281a5a7 | ||
|
|
f00710a57e | ||
|
|
c953904343 | ||
|
|
ddd36338da | ||
|
|
d76b6f878e | ||
|
|
f25ef8d682 | ||
|
|
33a2c01ea2 | ||
|
|
9c637df77e | ||
|
|
29072797ca | ||
|
|
db69ceade6 | ||
|
|
b9c248e7e7 | ||
|
|
d533b59ee3 | ||
|
|
5ad2980d69 | ||
|
|
ca36ac2ba1 | ||
|
|
ba675d6a55 | ||
|
|
e4c5a4f61b | ||
|
|
a8e52ad89f | ||
|
|
8cf4fb53aa | ||
|
|
1769ae54e4 | ||
|
|
e545679265 | ||
|
|
981080500b | ||
|
|
636d28b784 | ||
|
|
188783ac1a | ||
|
|
ff650d50a2 | ||
|
|
78689645dc | ||
|
|
26970579b8 | ||
|
|
04b4f53488 | ||
|
|
c310941f69 | ||
|
|
c02f146791 | ||
|
|
a631200cca | ||
|
|
6207a3f999 | ||
|
|
0bd01824f6 | ||
|
|
4465e2c46b | ||
|
|
34b309b99a | ||
|
|
c6725884bc | ||
|
|
a31738f6f1 | ||
|
|
39a2867ea8 | ||
|
|
468c3df82d | ||
|
|
d5b017407d | ||
|
|
4b044bd8e1 | ||
|
|
aeaf64b467 | ||
|
|
a137f817f5 | ||
|
|
ff87145537 | ||
|
|
667481c371 | ||
|
|
af71605769 | ||
|
|
4657a3bd0f | ||
|
|
cb7366f472 | ||
|
|
bf803e241f | ||
|
|
8a8d023f90 | ||
|
|
5b3aa45e58 | ||
|
|
c4403c523f | ||
|
|
07b89b2bad | ||
|
|
68b79f97f3 | ||
|
|
815d72065c | ||
|
|
e0dc721324 | ||
|
|
2f873edc9c | ||
|
|
d5f46a846a | ||
|
|
f5a923b9d1 | ||
|
|
36bab6ef06 | ||
|
|
5273900b0e | ||
|
|
1451344221 | ||
|
|
8ed9f779cd | ||
|
|
f548a966f5 | ||
|
|
67ad31b8b2 | ||
|
|
35f46eaa3f | ||
|
|
ccfb125f02 | ||
|
|
f29e3ce8d5 | ||
|
|
bcb4646d6b | ||
|
|
5b8af30da1 | ||
|
|
30fadaed7f | ||
|
|
c01282a322 | ||
|
|
0e3f18ed70 | ||
|
|
e5b1d30f1b | ||
|
|
6746b751c4 | ||
|
|
544c69c61c | ||
|
|
0ab66db7b9 | ||
|
|
f7669c201f | ||
|
|
0b3fbdba87 | ||
|
|
3dad837ca6 | ||
|
|
d7e78059b0 | ||
|
|
01f7abfc63 | ||
|
|
e57e89b9d5 | ||
|
|
bcc1f93370 | ||
|
|
b161d72326 | ||
|
|
66c013682f | ||
|
|
9f3ba737d6 | ||
|
|
f86f5ad224 | ||
|
|
8b6b3ff28a | ||
|
|
4a3dc2cea2 | ||
|
|
bd0cfffe23 | ||
|
|
8c5ea7ec55 | ||
|
|
412d1b0a01 | ||
|
|
642be502c0 | ||
|
|
5c4a42caa0 | ||
|
|
e847788569 | ||
|
|
86ca5b3f16 | ||
|
|
9b1fbdbca6 | ||
|
|
97391fd4b9 | ||
|
|
68eda78704 | ||
|
|
7c52a1c04b | ||
|
|
9d34fc394c | ||
|
|
973c9872f9 | ||
|
|
eef7ba48dd | ||
|
|
299ff2b23f | ||
|
|
f8bfea8bae | ||
|
|
e199c33c6e | ||
|
|
08296cd66d | ||
|
|
197b503f3e | ||
|
|
129eb178eb | ||
|
|
6b70f5e490 | ||
|
|
4aa6d39fd8 | ||
|
|
51adaf2c47 | ||
|
|
ea5b901bcc | ||
|
|
f1f19841bd | ||
|
|
655ad34414 | ||
|
|
017eecb8e8 | ||
|
|
b8e382b011 | ||
|
|
c9af358e4b | ||
|
|
3afcfe3330 | ||
|
|
b521d8564a | ||
|
|
53a13e8bc6 | ||
|
|
f3f5e2e162 | ||
|
|
ec41d849bb | ||
|
|
1d547fe273 | ||
|
|
b52d8fbebe | ||
|
|
b61a2c722c | ||
|
|
c455d2c1de | ||
|
|
77070f5ab9 | ||
|
|
8d5f7160ac | ||
|
|
b167ce2913 | ||
|
|
3ac15ca8d1 | ||
|
|
f703d4fc3e | ||
|
|
77c461fed7 | ||
|
|
a89c21b06a | ||
|
|
b74aa6b14c | ||
|
|
0c4a791111 | ||
|
|
1feedd4d9c | ||
|
|
0ec1808956 | ||
|
|
546e7cafa4 | ||
|
|
d9926a1c21 | ||
|
|
e50969e31c | ||
|
|
a6d9998e1d | ||
|
|
a42f9e4ee3 | ||
|
|
f16db6cd80 | ||
|
|
4943606f4a | ||
|
|
87629dda81 | ||
|
|
71928b473e | ||
|
|
a0bee9fa0a | ||
|
|
0defd927f3 | ||
|
|
6c41e8f63f | ||
|
|
fa67a4e956 | ||
|
|
bf9352fb26 | ||
|
|
7573d4e1ba | ||
|
|
bfb483898f | ||
|
|
f363788d76 | ||
|
|
714056f157 | ||
|
|
1abc96fa27 | ||
|
|
0fa98479ad | ||
|
|
02f2171010 | ||
|
|
a5e7d0a90f | ||
|
|
9fb5134f64 | ||
|
|
9aeb23dfe9 | ||
|
|
e59d8c9378 | ||
|
|
0616667688 | ||
|
|
9747cc9e58 | ||
|
|
7b44dea4b1 | ||
|
|
18aea922e9 | ||
|
|
7d194aceb6 | ||
|
|
2722b8a3df | ||
|
|
fd754ff8f8 | ||
|
|
b7738e7af3 | ||
|
|
99bebabac9 | ||
|
|
0ed48b364a | ||
|
|
b38b8b9c18 | ||
|
|
5434856136 | ||
|
|
aa1eb2f40d | ||
|
|
7260d7883c | ||
|
|
154250f551 | ||
|
|
3e4fb92cfb | ||
|
|
0da713e278 | ||
|
|
94cb1545b3 | ||
|
|
411527a240 | ||
|
|
7777057d35 | ||
|
|
dc5ef9ad01 | ||
|
|
7471f65d95 | ||
|
|
5d4b450c52 | ||
|
|
483b1f51c9 | ||
|
|
ecac7a629e | ||
|
|
21209ff46b | ||
|
|
716bdc36fd | ||
|
|
4f120f3714 | ||
|
|
dd20a3ce7e | ||
|
|
c727c86245 | ||
|
|
82b4ec22be | ||
|
|
501f1e6de0 | ||
|
|
414d5b648c | ||
|
|
a237e7f860 | ||
|
|
07a3bef25c | ||
|
|
2f51285349 | ||
|
|
64465c1318 | ||
|
|
a506adea41 | ||
|
|
f5ae1ce00b | ||
|
|
738d6f1040 | ||
|
|
ba372197fb | ||
|
|
bde27ccec3 | ||
|
|
d2cf74027c | ||
|
|
bc966577ff | ||
|
|
fdf4dff561 | ||
|
|
fab6cbed75 | ||
|
|
051eede1ed | ||
|
|
0696cac50b | ||
|
|
0c2485e86c | ||
|
|
7d696a7063 | ||
|
|
53573e950f | ||
|
|
1e99983263 | ||
|
|
5409e43fb5 | ||
|
|
f33f891c54 | ||
|
|
6e55552292 | ||
|
|
e569f82dd3 | ||
|
|
1a4940bbda | ||
|
|
22dd3c8048 | ||
|
|
04b7648e00 | ||
|
|
be0759d465 | ||
|
|
339d939873 | ||
|
|
c1950e40e6 | ||
|
|
ad76bad8fd | ||
|
|
55737e4ff6 | ||
|
|
f3b4ddee6c | ||
|
|
758b504cf3 | ||
|
|
416f7c2600 | ||
|
|
b9f02f22c4 | ||
|
|
b635610409 | ||
|
|
d4d4d6217f | ||
|
|
be47320a05 | ||
|
|
9f3ce7551a | ||
|
|
5f3b79e21d | ||
|
|
56c12bd1ea | ||
|
|
3a6da7c594 | ||
|
|
9909a80d6a | ||
|
|
a52cae1dba | ||
|
|
61b01f82ef | ||
|
|
75b847132a | ||
|
|
32ec672311 | ||
|
|
e2812f722d | ||
|
|
aa64a5328e | ||
|
|
59510f509a | ||
|
|
a35c7bc81a | ||
|
|
02769929b3 | ||
|
|
1a09003a6e | ||
|
|
a827714823 | ||
|
|
8f9e76ca42 | ||
|
|
478ace984a | ||
|
|
028fd268b8 | ||
|
|
876a095166 | ||
|
|
14c3ff58a4 | ||
|
|
2819b302c3 | ||
|
|
9fb82f9687 | ||
|
|
df80deb070 | ||
|
|
5fb72d7c8f | ||
|
|
f1ca17f6a1 | ||
|
|
fe7cda57e4 | ||
|
|
a3f9abcbd1 | ||
|
|
77041e2cd2 | ||
|
|
14235f171b | ||
|
|
1f90e3ce7f | ||
|
|
676d8bb624 | ||
|
|
4bcaf5d306 | ||
|
|
70d3ad3ca1 | ||
|
|
46debfb8b5 | ||
|
|
9a4b572d34 | ||
|
|
ddc75cc46b | ||
|
|
dafd0bc49a | ||
|
|
2cb912e4f4 | ||
|
|
91a513e87d | ||
|
|
efdb41f2a6 | ||
|
|
26b850c15c | ||
|
|
9941735ae7 | ||
|
|
29e5dc4413 | ||
|
|
3a660c5527 | ||
|
|
738991494a | ||
|
|
ee4c3ff1b8 | ||
|
|
60486bcc46 | ||
|
|
79eed5182e | ||
|
|
5de0f3f36a | ||
|
|
9f316e11bd | ||
|
|
c6d8f6da70 | ||
|
|
73fd98b82e | ||
|
|
6cb725391d | ||
|
|
c13facdaca | ||
|
|
e259474362 | ||
|
|
7740855a78 | ||
|
|
d605170f9f | ||
|
|
4734af747b | ||
|
|
5d17731b73 | ||
|
|
07a7fde825 | ||
|
|
049abef2d4 | ||
|
|
7c60b50a39 | ||
|
|
4f83c60296 | ||
|
|
cd5817b67e | ||
|
|
a4d580a6f8 | ||
|
|
ca43e634b5 | ||
|
|
85324f80fe | ||
|
|
56a7fcf189 | ||
|
|
8cd15a02cd | ||
|
|
ea771c17c5 | ||
|
|
65ebfb16c9 | ||
|
|
44a65fc5de | ||
|
|
f031eb1ef2 | ||
|
|
b50d79542c | ||
|
|
8351543c0f | ||
|
|
8add78ed5e | ||
|
|
4b3a3d6faa | ||
|
|
71d012fa61 | ||
|
|
114df1f137 | ||
|
|
00adf7ff17 | ||
|
|
3f0415fa24 | ||
|
|
348d0c944e | ||
|
|
e9ba5fcaf3 | ||
|
|
48097745f6 | ||
|
|
1091ca969f | ||
|
|
09d62cc630 | ||
|
|
a2ead993e6 | ||
|
|
b330aa8dd5 | ||
|
|
1b8053a618 | ||
|
|
70e5ab3fde | ||
|
|
f4490f6918 | ||
|
|
dadd987bf4 | ||
|
|
a1ced29279 | ||
|
|
791d6c1336 | ||
|
|
10abb07960 | ||
|
|
040a5ddad5 | ||
|
|
e7eb04954d | ||
|
|
09b00fa4e3 | ||
|
|
74842fd6d4 | ||
|
|
f23b47ba17 | ||
|
|
15eedfbb12 | ||
|
|
cfc25e08dc | ||
|
|
1583eb36e4 | ||
|
|
406452f019 | ||
|
|
12b139beaf | ||
|
|
2ed1c22227 | ||
|
|
eb23ef1747 | ||
|
|
970b10d01b | ||
|
|
61b938d6fa | ||
|
|
0c34555b02 | ||
|
|
83a988e2e4 | ||
|
|
91efe1cb5a | ||
|
|
735241f049 | ||
|
|
dd82370a80 | ||
|
|
dab052e53d | ||
|
|
322b061632 | ||
|
|
43847f2244 | ||
|
|
8f9ba96c59 | ||
|
|
10406c73b3 | ||
|
|
67a6a12916 | ||
|
|
139122c57f | ||
|
|
959d7ca9d0 | ||
|
|
3b568b0943 | ||
|
|
b2e1b38864 | ||
|
|
5c6fe97b35 | ||
|
|
2071070f39 | ||
|
|
89a78a5f3c | ||
|
|
bc23a6bb05 | ||
|
|
ac1983eae3 | ||
|
|
33c5fc95b4 | ||
|
|
6a953394f4 | ||
|
|
c152a88ff6 | ||
|
|
cbf57e7421 | ||
|
|
3f3ae48ff4 | ||
|
|
27ca0db7a6 | ||
|
|
49f34fbf68 | ||
|
|
fe6094a822 | ||
|
|
8a30c7992d | ||
|
|
70eb9335bd | ||
|
|
da80f91e1c | ||
|
|
cc60d193ce | ||
|
|
0f99410ba1 | ||
|
|
442dadd6fe | ||
|
|
da9dd58f34 | ||
|
|
bd01d80ffc | ||
|
|
7eda72d440 | ||
|
|
c75339edcc | ||
|
|
b0b7244b3a | ||
|
|
a9e849ff4f | ||
|
|
4c95a4d7c6 | ||
|
|
eac9fabd48 | ||
|
|
e2a08fa824 | ||
|
|
d403749d09 | ||
|
|
582c2105a9 | ||
|
|
f39a34ccfa | ||
|
|
08f5b9f1f9 | ||
|
|
793962c5b8 | ||
|
|
0d20130d07 | ||
|
|
617090cfda | ||
|
|
a9e604d51d | ||
|
|
e599b86424 | ||
|
|
da9e584921 | ||
|
|
e9d764d7ad | ||
|
|
6268955778 | ||
|
|
4ddfc6e9a2 | ||
|
|
dcbc05a9cd | ||
|
|
4c705334fa | ||
|
|
c12da599b9 | ||
|
|
f685a48008 | ||
|
|
7c15e41da5 | ||
|
|
7b540f0226 | ||
|
|
e29c22e662 | ||
|
|
f0d3b47ad8 | ||
|
|
bfe7533546 | ||
|
|
880bf17dae | ||
|
|
c9da3363a0 | ||
|
|
9bd4ed3e60 | ||
|
|
68fcc19565 | ||
|
|
8e4efd071e | ||
|
|
f2ad9f6fad | ||
|
|
968c5eb767 | ||
|
|
19424e95db | ||
|
|
0f9ef2506f | ||
|
|
1f1195975f | ||
|
|
87fce8ef27 | ||
|
|
d2a4f64fd6 | ||
|
|
a357d84dce | ||
|
|
48c96bbaea | ||
|
|
c666794ce3 | ||
|
|
1ee3df6976 | ||
|
|
d4dea53ae9 | ||
|
|
d477a9222e | ||
|
|
69a9d29190 | ||
|
|
e2425592b6 | ||
|
|
964b2661d8 | ||
|
|
8b65b524d5 | ||
|
|
f1ac2cd336 | ||
|
|
e73d0df2b6 | ||
|
|
af3f483924 | ||
|
|
809b0eb1f5 | ||
|
|
b5f3bfce92 | ||
|
|
aeb2baa495 | ||
|
|
ea9aff1d3f | ||
|
|
cecae419fb | ||
|
|
df4db50904 | ||
|
|
decc4002a0 | ||
|
|
5bb563e3bc | ||
|
|
9be82891b0 | ||
|
|
c97685fe6c | ||
|
|
358b671033 | ||
|
|
92a4bf4441 | ||
|
|
5465647c2e | ||
|
|
a2baeed329 | ||
|
|
92aa464edb | ||
|
|
da29298d4d | ||
|
|
afcf0795c4 | ||
|
|
394e2815db | ||
|
|
f3bbe4bb1f | ||
|
|
2a3a18a283 | ||
|
|
431c80f4f0 | ||
|
|
9ac18c39a1 | ||
|
|
d1b7473418 | ||
|
|
1ad52f131c | ||
|
|
8064bbbc3f | ||
|
|
64e898f6db | ||
|
|
1732166328 | ||
|
|
8edde7a4b3 | ||
|
|
d11a0c9f14 | ||
|
|
7be2abe6b5 | ||
|
|
4a62cd4ad8 | ||
|
|
6d21b9448a | ||
|
|
2d1a3073f6 | ||
|
|
d7b9886387 | ||
|
|
1543e529f1 | ||
|
|
4043f320fe | ||
|
|
32b834aa04 | ||
|
|
b43ec3d6f0 | ||
|
|
316a25dead | ||
|
|
bfe020e06c | ||
|
|
5aae560683 | ||
|
|
86e72ffefb | ||
|
|
6416198e9f | ||
|
|
3c4cd9d08b | ||
|
|
fdb02ee5ca | ||
|
|
35f85ddd86 | ||
|
|
e30fa7d15e | ||
|
|
c476305149 | ||
|
|
45ad7696fe | ||
|
|
d86cd4e4b4 | ||
|
|
0e84d71559 | ||
|
|
5f14704eee | ||
|
|
52dca859c7 | ||
|
|
f544316314 | ||
|
|
ecdebb315f | ||
|
|
1f523be72d | ||
|
|
f2a20a0a80 | ||
|
|
e5a2efaa65 | ||
|
|
c44cb3246d | ||
|
|
9cfd9d7ce0 | ||
|
|
66d90dd412 | ||
|
|
d5dfe7d457 | ||
|
|
0db0b4e302 | ||
|
|
635d6a19ef | ||
|
|
777844ae0c | ||
|
|
a10a4ad900 | ||
|
|
c330d694c7 | ||
|
|
73555f9088 | ||
|
|
e1ec65bd53 | ||
|
|
65b8e52d80 | ||
|
|
4b7a418e28 | ||
|
|
73d917b76e | ||
|
|
527a6003e2 | ||
|
|
26c731a382 | ||
|
|
8297563978 | ||
|
|
1da0526072 | ||
|
|
635a67afac | ||
|
|
c7e7c819a2 | ||
|
|
fbf677d9b2 | ||
|
|
d37b788ea8 | ||
|
|
cb336ff666 | ||
|
|
e2e5433beb | ||
|
|
c7b1038a9d | ||
|
|
d43aa12987 | ||
|
|
ca69ad8beb | ||
|
|
e42637226f | ||
|
|
ae25582178 | ||
|
|
5d85792178 | ||
|
|
acba9c0f61 | ||
|
|
ad7302a032 | ||
|
|
9f3bead99f | ||
|
|
38afc89407 | ||
|
|
c161bb287c | ||
|
|
06d7169674 | ||
|
|
8bf1640932 | ||
|
|
da50c888fe | ||
|
|
62dc8e4131 | ||
|
|
dc554ca61f | ||
|
|
cbb9149a82 | ||
|
|
dab7348da8 | ||
|
|
378e8a6224 | ||
|
|
289fdcc52b | ||
|
|
83e6e3e31f | ||
|
|
c387031252 | ||
|
|
403bd77eff | ||
|
|
b397c46184 | ||
|
|
0a6c4c31b2 | ||
|
|
02fcf184b5 | ||
|
|
b978126bb0 | ||
|
|
9f51d19087 | ||
|
|
859184000f | ||
|
|
1ad2f18c9e | ||
|
|
06a49a5f9e | ||
|
|
b811927e0e | ||
|
|
83cdd0a0c8 | ||
|
|
53c69cd2ce | ||
|
|
c6cf8e992d | ||
|
|
0c53e8c2d0 | ||
|
|
e1901e97c2 | ||
|
|
d694150372 | ||
|
|
4ab586aaf1 | ||
|
|
8a5209ad93 | ||
|
|
308fdf6e9a | ||
|
|
9f4e96fdfa | ||
|
|
c36af35730 | ||
|
|
424f478c3f | ||
|
|
5e0c272061 | ||
|
|
d99bdca094 | ||
|
|
187069bec9 | ||
|
|
01de768f09 | ||
|
|
73a861abf1 | ||
|
|
b4f60ee95c | ||
|
|
85e587a882 | ||
|
|
d91f6dc1b5 | ||
|
|
78c0d8cc88 | ||
|
|
b23d2ad43b | ||
|
|
ef68946e74 | ||
|
|
a5574f9189 | ||
|
|
ba61a92897 | ||
|
|
68fc3704e9 | ||
|
|
69867015e9 | ||
|
|
923f18ef76 | ||
|
|
7e8d254b95 | ||
|
|
6325c3f14a | ||
|
|
074a23e6b4 | ||
|
|
2fb61534a1 | ||
|
|
12b6c6ccf7 | ||
|
|
55ed7bd34d | ||
|
|
eeeef3eab5 | ||
|
|
96413b1604 | ||
|
|
2278fe42e5 | ||
|
|
3a183f869f | ||
|
|
6c5828822c | ||
|
|
9dd824ba50 | ||
|
|
d744ea3fca | ||
|
|
07352e82f1 | ||
|
|
1003cc9bf9 | ||
|
|
2b717997e2 | ||
|
|
20ebacf489 | ||
|
|
c6ef0e8887 | ||
|
|
f761d583b5 | ||
|
|
cee1ac150e | ||
|
|
71b47ae3ef | ||
|
|
36e3fda554 | ||
|
|
2b44a2c9bd | ||
|
|
af3b454805 | ||
|
|
3a0f3eaa50 | ||
|
|
80eb8d9e26 | ||
|
|
30c4136c4d | ||
|
|
83e4475021 | ||
|
|
c071d6d9f6 | ||
|
|
1df426aa5c | ||
|
|
023482406f | ||
|
|
04ba40981f | ||
|
|
fc10bdf24d | ||
|
|
28ad79c97a | ||
|
|
7661ad6653 | ||
|
|
e1436063ff | ||
|
|
3e11a28cd9 | ||
|
|
3ed80f3139 | ||
|
|
a698cc8112 | ||
|
|
916aaacb68 | ||
|
|
aaeb8150b7 | ||
|
|
5ba90cd8ea | ||
|
|
e9127523db | ||
|
|
92004f2e7e | ||
|
|
0eefc7a43c | ||
|
|
3a666702af | ||
|
|
3c0c939689 | ||
|
|
413f7c64e5 | ||
|
|
6f47c1ca20 | ||
|
|
abeebfe070 | ||
|
|
1a002eb23d | ||
|
|
71bc15aca1 | ||
|
|
65b24ce557 | ||
|
|
cfdd5edc49 | ||
|
|
b9035d3e0a | ||
|
|
e504913b0f | ||
|
|
af4133e3c9 | ||
|
|
a788b7e747 | ||
|
|
bb288f0284 | ||
|
|
f90abe9530 | ||
|
|
46bab7e4b9 | ||
|
|
22fb49fa90 | ||
|
|
957d187ae4 | ||
|
|
b88ea6735b | ||
|
|
51de8fd10a | ||
|
|
2ef6052536 | ||
|
|
394b1e6351 | ||
|
|
c95d068ef5 | ||
|
|
dbda35ffe1 | ||
|
|
6396e23a2a | ||
|
|
529b4bd185 | ||
|
|
85b51e4e77 | ||
|
|
e53a25cbae | ||
|
|
41e7145973 | ||
|
|
db238cc23f | ||
|
|
75e9c798e0 | ||
|
|
99527051b5 | ||
|
|
bed98303a3 | ||
|
|
d2679f59c5 | ||
|
|
4ee147ea34 | ||
|
|
f1ca61d7d7 | ||
|
|
a80b5cf19b | ||
|
|
44ac84767e | ||
|
|
a40d7ae4dd |
134
.gitignore
vendored
134
.gitignore
vendored
@@ -6,74 +6,78 @@
|
||||
*.lo
|
||||
*.o
|
||||
*.exe
|
||||
.deps
|
||||
.dirstamp
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
compile
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.mk
|
||||
config.status
|
||||
config.sub
|
||||
config_detected.h
|
||||
config_detected.mk
|
||||
configure
|
||||
configure.lineno
|
||||
depcomp
|
||||
depmode
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
mkinstalldirs
|
||||
/test-driver
|
||||
mpd
|
||||
mpd.service
|
||||
stamp-h1
|
||||
tags
|
||||
|
||||
*~
|
||||
|
||||
.#*
|
||||
.stgit*
|
||||
src/dsd2pcm/dsd2pcm
|
||||
src/win32/mpd_win32_rc.rc
|
||||
doc/doxygen.conf
|
||||
doc/protocol.html
|
||||
doc/protocol
|
||||
doc/user
|
||||
doc/developer
|
||||
doc/sticker
|
||||
doc/api
|
||||
test/software_volume
|
||||
test/run_convert
|
||||
test/run_decoder
|
||||
test/read_tags
|
||||
test/run_filter
|
||||
test/run_encoder
|
||||
test/run_output
|
||||
test/read_conf
|
||||
test/run_input
|
||||
test/read_mixer
|
||||
test/dump_playlist
|
||||
test/run_normalize
|
||||
test/tmp
|
||||
test/run_inotify
|
||||
test/test_queue_priority
|
||||
test/run_ntp_server
|
||||
test/run_resolver
|
||||
test/run_tcp_connect
|
||||
test/test_pcm
|
||||
test/dump_rva2
|
||||
test/dump_text_file
|
||||
test/test_util
|
||||
test/test_byte_reverse
|
||||
test/test_mixramp
|
||||
test/test_vorbis_encoder
|
||||
test/DumpDatabase
|
||||
.deps
|
||||
.dirstamp
|
||||
|
||||
tags
|
||||
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/aclocal.m4
|
||||
/autom4te.cache
|
||||
/config.h
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.mk
|
||||
/config.status
|
||||
/config_detected.h
|
||||
/config_detected.mk
|
||||
/configure
|
||||
/configure.lineno
|
||||
/depmode
|
||||
/libtool
|
||||
/ltmain.sh
|
||||
/mkinstalldirs
|
||||
/build
|
||||
/src/mpd
|
||||
/systemd/mpd.service
|
||||
/stamp-h1
|
||||
|
||||
/src/dsd2pcm/dsd2pcm
|
||||
/src/win32/mpd_win32_rc.rc
|
||||
|
||||
/doc/doxygen.conf
|
||||
/doc/protocol.html
|
||||
/doc/protocol
|
||||
/doc/user
|
||||
/doc/developer
|
||||
/doc/sticker
|
||||
/doc/api
|
||||
|
||||
/test/software_volume
|
||||
/test/run_convert
|
||||
/test/run_decoder
|
||||
/test/read_tags
|
||||
/test/run_filter
|
||||
/test/run_encoder
|
||||
/test/run_output
|
||||
/test/read_conf
|
||||
/test/run_input
|
||||
/test/read_mixer
|
||||
/test/dump_playlist
|
||||
/test/run_normalize
|
||||
/test/tmp
|
||||
/test/run_inotify
|
||||
/test/test_queue_priority
|
||||
/test/test_protocol
|
||||
/test/run_ntp_server
|
||||
/test/run_resolver
|
||||
/test/run_tcp_connect
|
||||
/test/test_pcm
|
||||
/test/dump_rva2
|
||||
/test/dump_text_file
|
||||
/test/test_util
|
||||
/test/test_byte_reverse
|
||||
/test/test_mixramp
|
||||
/test/test_vorbis_encoder
|
||||
/test/DumpDatabase
|
||||
|
||||
/lib/
|
||||
|
||||
/*.tar.gz
|
||||
/*.tar.bz2
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -1,5 +1,5 @@
|
||||
Music Player Daemon - http://www.musicpd.org
|
||||
Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
|
||||
The following people have contributed code to MPD:
|
||||
|
||||
|
||||
5
INSTALL
5
INSTALL
@@ -16,6 +16,8 @@ gcc 4.6 or later - http://gcc.gnu.org/
|
||||
clang 3.2 or later - http://clang.llvm.org/
|
||||
Any other C++11 compliant compiler should also work.
|
||||
|
||||
Boost 1.46 - http://www.boost.org/
|
||||
|
||||
GLib 2.28 - http://www.gtk.org/
|
||||
General-purpose utility library.
|
||||
|
||||
@@ -114,9 +116,6 @@ For WavPack playback.
|
||||
libadplug - http://adplug.sourceforge.net/
|
||||
For AdLib playback.
|
||||
|
||||
despotify - https://github.com/SimonKagstrom/despotify
|
||||
For Spotify playback.
|
||||
|
||||
|
||||
Optional Miscellaneous Dependencies
|
||||
-----------------------------------
|
||||
|
||||
1545
Makefile.am
1545
Makefile.am
File diff suppressed because it is too large
Load Diff
298
NEWS
298
NEWS
@@ -1,6 +1,304 @@
|
||||
ver 0.19.15 (2016/04/30)
|
||||
* decoder
|
||||
- ffmpeg: support FFmpeg 3.0
|
||||
- ffmpeg: use as fallback instead of "mad" if no plugin matches
|
||||
- opus: support bigger OpusTags packets
|
||||
* fix more build failures on non-glibc builds due to constexpr Mutex
|
||||
* fix build failure due to missing include
|
||||
* fix unit test on Alpha
|
||||
|
||||
ver 0.19.14 (2016/03/18)
|
||||
* decoder
|
||||
- dsdiff: fix off-by-one buffer overflow
|
||||
- opus: limit tag size to 64 kB
|
||||
* archive
|
||||
- iso9660: fix buffer overflow
|
||||
* fix quadratic runtime bug in the tag pool
|
||||
* fix build failures on non-glibc builds due to constexpr Mutex
|
||||
|
||||
ver 0.19.13 (2016/02/23)
|
||||
* tags
|
||||
- aiff, riff: fix ID3 chunk padding
|
||||
* decoder
|
||||
- ffmpeg: support the TAK codec
|
||||
* fix disappearing duration of remote songs during playback
|
||||
* initialize supplementary groups with glibc 2.19+
|
||||
|
||||
ver 0.19.12 (2015/12/15)
|
||||
* fix assertion failure on malformed UTF-8 tag
|
||||
* fix build failure on non-Linux systems
|
||||
* fix LimitRTTIME in systemd unit file
|
||||
|
||||
ver 0.19.11 (2015/10/27)
|
||||
* tags
|
||||
- ape: fix buffer overflow
|
||||
* decoder
|
||||
- ffmpeg: fix crash due to wrong avio_alloc_context() call
|
||||
- gme: don't loop forever, fall back to GME's default play length
|
||||
* encoder
|
||||
- flac: fix crash with 32 bit playback
|
||||
* mixer
|
||||
- fix mixer lag after enabling/disabling output
|
||||
|
||||
ver 0.19.10 (2015/06/21)
|
||||
* input
|
||||
- curl: fix deadlock on small responses
|
||||
- smbclient: fix DFF playback
|
||||
* decoder
|
||||
- ffmpeg: improve seeking accuracy
|
||||
- fix stuck stream tags
|
||||
* encoder
|
||||
- opus: fix bogus granulepos
|
||||
* output
|
||||
- fix failure to open device right after booting
|
||||
* neighbor
|
||||
- nfs: fix deadlock when connecting
|
||||
* fix "single" mode breakage due to queue edits
|
||||
|
||||
ver 0.19.9 (2015/02/06)
|
||||
* decoder
|
||||
- dsdiff, dsf: raise ID3 tag limit to 1 MB
|
||||
* playlist: fix loading duplicate tag types from state file
|
||||
* despotify: remove defunct plugin
|
||||
* fix clock integer overflow on OS X
|
||||
* fix gcc 5.0 warnings
|
||||
* fix build failure with uClibc
|
||||
* fix build failure on non-POSIX operating systems
|
||||
* fix dependency issue on parallel Android build
|
||||
* fix database/state file saving on Windows
|
||||
|
||||
ver 0.19.8 (2015/01/14)
|
||||
* input
|
||||
- curl: fix bug after rewinding from end-of-file
|
||||
- mms: reduce delay at the beginning of playback
|
||||
* decoder
|
||||
- dsdiff, dsf: allow ID3 tags larger than 4 kB
|
||||
- ffmpeg: support interleaved floating point
|
||||
* fix clang 3.6 warnings
|
||||
* fix build failure on NetBSD
|
||||
|
||||
ver 0.19.7 (2014/12/17)
|
||||
* input
|
||||
- nfs: fix crash while canceling a failing file open operation
|
||||
- nfs: fix memory leak on connection failure
|
||||
- nfs: fix reconnect after mount failure
|
||||
- nfs: implement mount timeout (60 seconds)
|
||||
* storage
|
||||
- nfs: implement I/O timeout (60 seconds)
|
||||
* playlist
|
||||
- embcue: fix filename suffix detection
|
||||
- don't skip non-existent songs in "listplaylist"
|
||||
* decoder
|
||||
- ffmpeg: fix time stamp underflow
|
||||
* fix memory allocator bug on Windows
|
||||
|
||||
ver 0.19.6 (2014/12/08)
|
||||
* decoder
|
||||
- ffmpeg: support FFmpeg 2.5
|
||||
* fix build failure with musl
|
||||
* android
|
||||
- update libFLAC to 1.3.1
|
||||
- update FFmpeg to 2.5
|
||||
|
||||
ver 0.19.5 (2014/11/26)
|
||||
* input
|
||||
- nfs: fix crash on connection failure
|
||||
* archive
|
||||
- zzip: fix crash after seeking
|
||||
* decoder
|
||||
- dsdiff, dsf, opus: fix deadlock while seeking
|
||||
- mp4v2: remove because of incompatible license
|
||||
|
||||
ver 0.19.4 (2014/11/18)
|
||||
* protocol
|
||||
- workaround for buggy clients that send "add /"
|
||||
* decoder
|
||||
- ffmpeg: support opus
|
||||
- opus: add MIME types audio/ogg and application/ogg
|
||||
* fix crash on failed filename charset conversion
|
||||
* fix local socket detection from uid=0 (root)
|
||||
|
||||
ver 0.19.3 (2014/11/11)
|
||||
* protocol
|
||||
- fix "(null)" result string to "list" when AlbumArtist is disabled
|
||||
* database
|
||||
- upnp: fix breakage due to malformed URIs
|
||||
* input
|
||||
- curl: another fix for redirected streams
|
||||
* decoder
|
||||
- audiofile: fix crash while playing streams
|
||||
- audiofile: fix bit rate calculation
|
||||
- ffmpeg: support opus
|
||||
- opus: fix bogus duration on streams
|
||||
- opus: support chained streams
|
||||
- opus: improved error logging
|
||||
* fix distorted audio with soxr resampler
|
||||
* fix build failure on Mac OS X with non-Apple compilers
|
||||
|
||||
ver 0.19.2 (2014/11/02)
|
||||
* input
|
||||
- curl: fix redirected streams
|
||||
* playlist
|
||||
- don't allow empty playlist name
|
||||
- m3u: don't ignore unterminated last line
|
||||
- m3u: recognize the file suffix ".m3u8"
|
||||
* decoder
|
||||
- ignore URI query string for plugin detection
|
||||
- faad: remove workaround for ancient libfaad2 ABI bug
|
||||
- ffmpeg: recognize MIME type audio/aacp
|
||||
- mad: fix negative replay gain values
|
||||
* output
|
||||
- fix memory leak after filter initialization error
|
||||
- fall back to PCM if given DSD sample rate is not supported
|
||||
* fix assertion failure on unsupported PCM conversion
|
||||
* auto-disable plugins that require GLib when --disable-glib is used
|
||||
|
||||
ver 0.19.1 (2014/10/19)
|
||||
* input
|
||||
- mms: fix deadlock bug
|
||||
* playlist
|
||||
- extm3u: fix Extended M3U detection
|
||||
- m3u, extm3u, cue: fix truncated lines
|
||||
* fix build failure on Mac OS X
|
||||
* add missing file systemd/mpd.socket to tarball
|
||||
|
||||
ver 0.19 (2014/10/10)
|
||||
* protocol
|
||||
- new commands "addtagid", "cleartagid", "listfiles", "listmounts",
|
||||
"listneighbors", "mount", "rangeid", "unmount"
|
||||
- "lsinfo" and "readcomments" allowed for remote files
|
||||
- "listneighbors" lists file servers on the local network
|
||||
- "playlistadd" supports file:///
|
||||
- "idle" with unrecognized event name fails
|
||||
- "list" on album artist falls back to the artist tag
|
||||
- "list" and "count" allow grouping
|
||||
- new "search"/"find" filter "modified-since"
|
||||
- "seek*" allows fractional position
|
||||
- close connection after syntax error
|
||||
* database
|
||||
- proxy: forward "idle" events
|
||||
- proxy: forward the "update" command
|
||||
- proxy: copy "Last-Modified" from remote directories
|
||||
- simple: compress the database file using gzip
|
||||
- upnp: new plugin
|
||||
- cancel the update on shutdown
|
||||
* storage
|
||||
- music_directory can point to a remote file server
|
||||
- nfs: new plugin
|
||||
- smbclient: new plugin
|
||||
* playlist
|
||||
- cue: fix bogus duration of the last track
|
||||
- cue: restore CUE tracks from state file
|
||||
- soundcloud: use https instead of http
|
||||
- soundcloud: add default API key
|
||||
* archive
|
||||
- read tags from songs in an archive
|
||||
* input
|
||||
- alsa: new input plugin
|
||||
- curl: options "verify_peer" and "verify_host"
|
||||
- ffmpeg: update offset after seeking
|
||||
- ffmpeg: improved error messages
|
||||
- mms: non-blocking I/O
|
||||
- nfs: new input plugin
|
||||
- smbclient: new input plugin
|
||||
* filter
|
||||
- volume: improved software volume dithering
|
||||
* decoder:
|
||||
- vorbis, flac, opus: honor DESCRIPTION= tag in Xiph-based files as a comment to the song
|
||||
- audiofile: support scanning remote files
|
||||
- audiofile: log libaudiofile errors
|
||||
- dsdiff, dsf: report bit rate
|
||||
- dsdiff, dsf: implement seeking
|
||||
- dsf: support DSD512
|
||||
- dsf: support multi-channel files
|
||||
- dsf: fix big-endian bugs
|
||||
- dsf: fix noise at end of malformed file
|
||||
- mpg123: support ID3v2, ReplayGain and MixRamp
|
||||
- sndfile: support scanning remote files
|
||||
- sndfile: support tags "comment", "album", "track", "genre"
|
||||
- sndfile: native floating point playback
|
||||
- sndfile: optimized 16 bit playback
|
||||
- mp4v2: support playback of MP4 files.
|
||||
* encoder:
|
||||
- shine: new encoder plugin
|
||||
* output
|
||||
- alsa: support native DSD playback
|
||||
- alsa: rename "DSD over USB" to "DoP"
|
||||
- osx: fix hang after (un)plugging headphones
|
||||
* threads:
|
||||
- the update thread runs at "idle" priority
|
||||
- the output thread runs at "real-time" priority
|
||||
- increase kernel timer slack on Linux
|
||||
- name each thread (for debugging)
|
||||
* configuration
|
||||
- allow playlist directory without music directory
|
||||
- use XDG to auto-detect "music_directory" and "db_file"
|
||||
* add tags "AlbumSort", "MUSICBRAINZ_RELEASETRACKID"
|
||||
* disable global Latin-1 fallback for tag values
|
||||
* new resampler option using libsoxr
|
||||
* ARM NEON optimizations
|
||||
* install systemd unit for socket activation
|
||||
* Android port
|
||||
|
||||
ver 0.18.23 (2015/02/06)
|
||||
* despotify: remove defunct plugin
|
||||
* fix clock integer overflow on OS X
|
||||
* fix gcc 5.0 warnings
|
||||
|
||||
ver 0.18.22 (2015/01/14)
|
||||
* fix clang 3.6 warnings
|
||||
|
||||
ver 0.18.21 (2014/12/17)
|
||||
* playlist
|
||||
- embcue: fix filename suffix detection
|
||||
* decoder
|
||||
- ffmpeg: fix time stamp underflow
|
||||
|
||||
ver 0.18.20 (2014/12/08)
|
||||
* decoder
|
||||
- ffmpeg: support FFmpeg 2.5
|
||||
* fix build failure with musl
|
||||
|
||||
ver 0.18.19 (2014/11/26)
|
||||
* archive
|
||||
- zzip: fix crash after seeking
|
||||
|
||||
ver 0.18.18 (2014/11/18)
|
||||
* decoder
|
||||
- ffmpeg: support opus
|
||||
* fix crash on failed filename charset conversion
|
||||
* fix local socket detection from uid=0 (root)
|
||||
|
||||
ver 0.18.17 (2014/11/02)
|
||||
* playlist
|
||||
- don't allow empty playlist name
|
||||
- m3u: recognize the file suffix ".m3u8"
|
||||
* decoder
|
||||
- ignore URI query string for plugin detection
|
||||
- faad: remove workaround for ancient libfaad2 ABI bug
|
||||
- ffmpeg: recognize MIME type audio/aacp
|
||||
|
||||
ver 0.18.16 (2014/09/26)
|
||||
* fix DSD breakage due to typo in configure.ac
|
||||
|
||||
ver 0.18.15 (2014/09/26)
|
||||
* command
|
||||
- list: reset used size after the list has been processed
|
||||
* fix MixRamp
|
||||
* work around build failure on NetBSD
|
||||
|
||||
ver 0.18.14 (2014/09/11)
|
||||
* protocol
|
||||
- fix range parser bug on certain 32 bit architectures
|
||||
* decoder
|
||||
- audiofile: fix crash after seeking
|
||||
- ffmpeg: fix crash with ffmpeg/libav version 11
|
||||
- fix assertion failure after seeking
|
||||
|
||||
ver 0.18.13 (2014/08/31)
|
||||
* protocol
|
||||
- don't change song on "seekcur" in random mode
|
||||
|
||||
* decoder
|
||||
- dsdiff, dsf: fix endless loop on malformed file
|
||||
- ffmpeg: support ffmpeg/libav version 11
|
||||
|
||||
92
UPGRADING
92
UPGRADING
@@ -1,92 +0,0 @@
|
||||
Music Player Daemon (MPD) - UPGRADING
|
||||
|
||||
Upgrading to 0.14
|
||||
-----------------
|
||||
|
||||
The filesystem character set is determined by GLib, if it is not
|
||||
configured. GLib has an affinity towards UTF-8, while older MPD
|
||||
versions used to choose ISO-Latin-1.
|
||||
|
||||
|
||||
Upgrading to 0.13.0
|
||||
-------------------
|
||||
|
||||
JACK, Avahi, and libsamplerate have been added as optional dependencies.
|
||||
FLAC/OggFLAC now supports the 1.1.3 API, and libmikmod 3.2.0 betas are
|
||||
supported as well.
|
||||
|
||||
New mpd.conf parameters include zeroconf_name, samplerate_converter, and
|
||||
gapless_mp3_playback. See the mpd.conf man page or updated mpconf.example for
|
||||
more information on these parameters.
|
||||
|
||||
Support for the ID3v2 "Original Artist/Performer" tag has been added. Your
|
||||
MP3s will need to be rescanned for these tags to be included in the database.
|
||||
This can be done by running mpd --create-db.
|
||||
|
||||
Upgrading to 0.12.0
|
||||
-------------------
|
||||
|
||||
The ao_driver and ao_driver_options config parameters have been removed and
|
||||
replaced with the audio_output config section. You will have to update your
|
||||
config file to use this instead. See the mpd.conf man page or the new
|
||||
mpdconf.example for details on specifying audio_output sections.
|
||||
|
||||
The db_file parameter is no longer optional. If you did not specify it in your
|
||||
old config file then you will have to add it in order to run 0.12.0.
|
||||
|
||||
Support for OggFLAC and Musepack audio files has been added. Additionally,
|
||||
scanning of MP3 files has been improved. To make use of these updates it is
|
||||
highly recommended that you run mpd --create-db to recreate your entire
|
||||
database.
|
||||
|
||||
Upgrading to 0.11.0
|
||||
-------------------
|
||||
|
||||
The database format has changed a little bit, but in a backward compatible way.
|
||||
This means that if you upgrade to 0.11.0 from 0.10.x, you do not need to make
|
||||
any changes. However, if you downgrade back to 0.10.x, then you will need
|
||||
to recreate your db.
|
||||
|
||||
The default port for MPD is now 6600, so update your mpd and client
|
||||
configurations appropriately.
|
||||
|
||||
Upgrading to 0.10.0
|
||||
-------------------
|
||||
|
||||
All information is now stored in the db in UTF-8 format, and the character
|
||||
set used for the filesystem is stored in the db. Thus, it is highly
|
||||
recommended that you recreate the db. To do so, run mpd with the
|
||||
"--create-db" command line option. Also, note that the filesystem
|
||||
character set will be determined from your current locale settings.
|
||||
If your locale settings are not the same as those used for the filesystem,
|
||||
then use the config file parameter "filesystem_charset" to specify the
|
||||
correct character set (this maybe necessary if you create the db with root).
|
||||
|
||||
Upgrading to 0.9.3
|
||||
------------------
|
||||
|
||||
Wave support was added, so to have your wave files added, update the db (mpc
|
||||
update).
|
||||
|
||||
Also, song lengths are now stored in the db. To get this stuff
|
||||
added to the db, you will need to recreate the db from scratch. To do this,
|
||||
run mpd with the "--create-db" commandline option.
|
||||
|
||||
Upgrading to 0.9.0
|
||||
------------------
|
||||
|
||||
The "stop_on_error" config parameter was removed, so be sure to remove this
|
||||
parameter from your config file.
|
||||
|
||||
Upgrading to 0.8.x
|
||||
------------------
|
||||
|
||||
If you have FLACs, then to have them added to your list of available music,
|
||||
just use "update".
|
||||
|
||||
Upgrading from 0.5.x to 0.6.x
|
||||
-----------------------------
|
||||
If you have not compiled MPD with "make ogg", then nothing is needed.
|
||||
|
||||
If you compiled with "make ogg", just use "update" (available via the phpMp
|
||||
interface) to add your OGGs to MPD's list of available music.
|
||||
1
android/.gitignore
vendored
Normal file
1
android/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
24
android/AndroidManifest.xml
Normal file
24
android/AndroidManifest.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="13"
|
||||
android:versionName="0.19.9">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
||||
<activity android:name=".Main"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
433
android/build.py
Executable file
433
android/build.py
Executable file
@@ -0,0 +1,433 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os, os.path
|
||||
import sys, shutil, subprocess
|
||||
import urllib.request
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: build.py SDK_PATH NDK_PATH [configure_args...]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
sdk_path = sys.argv[1]
|
||||
ndk_path = sys.argv[2]
|
||||
configure_args = sys.argv[3:]
|
||||
|
||||
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
|
||||
print("SDK not found in", ndk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isdir(ndk_path):
|
||||
print("NDK not found in", ndk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# the path to the MPD sources
|
||||
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
|
||||
|
||||
# output directories
|
||||
lib_path = os.path.abspath('lib')
|
||||
tarball_path = lib_path
|
||||
src_path = os.path.join(lib_path, 'src')
|
||||
build_path = os.path.join(lib_path, 'build')
|
||||
root_path = os.path.join(lib_path, 'root')
|
||||
|
||||
# build host configuration
|
||||
build_arch = 'linux-x86_64'
|
||||
|
||||
# redirect pkg-config to use our root directory instead of the default
|
||||
# one on the build host
|
||||
os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(root_path, 'lib/pkgconfig')
|
||||
|
||||
# select the NDK compiler
|
||||
gcc_version = '4.9'
|
||||
llvm_version = '3.5'
|
||||
|
||||
# select the NDK target
|
||||
ndk_arch = 'arm'
|
||||
host_arch = 'arm-linux-androideabi'
|
||||
android_abi = 'armeabi-v7a'
|
||||
ndk_platform = 'android-14'
|
||||
|
||||
# set up the NDK toolchain
|
||||
|
||||
gcc_toolchain = os.path.join(ndk_path, 'toolchains', host_arch + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
llvm_toolchain = os.path.join(ndk_path, 'toolchains', 'llvm-' + llvm_version, 'prebuilt', build_arch)
|
||||
ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
|
||||
target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
|
||||
|
||||
llvm_triple = 'armv7-none-linux-androideabi'
|
||||
|
||||
def select_toolchain(use_cxx, use_clang):
|
||||
global cc, cxx, ar, strip, cflags, cxxflags, cppflags, ldflags, libs
|
||||
|
||||
target_arch = '-march=armv7-a -mfloat-abi=softfp'
|
||||
if use_clang:
|
||||
cc = os.path.join(llvm_toolchain, 'bin/clang')
|
||||
cxx = os.path.join(llvm_toolchain, 'bin/clang++')
|
||||
target_arch += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + gcc_toolchain
|
||||
else:
|
||||
cc = os.path.join(gcc_toolchain, 'bin', host_arch + '-gcc')
|
||||
cxx = os.path.join(gcc_toolchain, 'bin', host_arch + '-g++')
|
||||
ar = os.path.join(gcc_toolchain, 'bin', host_arch + '-ar')
|
||||
strip = os.path.join(gcc_toolchain, 'bin', host_arch + '-strip')
|
||||
|
||||
libstdcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/gnu-libstdc++', gcc_version)
|
||||
libstdcxx_cppflags = '-isystem ' + os.path.join(libstdcxx_path, 'include') + ' -isystem ' + os.path.join(libstdcxx_path, 'libs', android_abi, 'include')
|
||||
if use_clang:
|
||||
libstdcxx_cppflags += ' -D__STRICT_ANSI__'
|
||||
libstdcxx_ldadd = os.path.join(libstdcxx_path, 'libs', android_abi, 'libgnustl_static.a')
|
||||
|
||||
cflags = '-Os -g ' + target_arch
|
||||
cxxflags = '-Os -g ' + target_arch
|
||||
cppflags = '--sysroot=' + target_root + ' -I' + root_path + '/include'
|
||||
ldflags = '--sysroot=' + target_root + ' -L' + root_path + '/lib'
|
||||
libs = ''
|
||||
|
||||
if use_cxx:
|
||||
libs += ' ' + libstdcxx_ldadd
|
||||
cppflags += ' ' + libstdcxx_cppflags
|
||||
|
||||
def file_md5(path):
|
||||
"""Calculate the MD5 checksum of a file and return it in hexadecimal notation."""
|
||||
|
||||
with open(path, 'rb') as f:
|
||||
m = hashlib.md5()
|
||||
while True:
|
||||
data = f.read(65536)
|
||||
if len(data) == 0:
|
||||
# end of file
|
||||
return m.hexdigest()
|
||||
m.update(data)
|
||||
|
||||
def download_tarball(url, md5):
|
||||
"""Download a tarball, verify its MD5 checksum and return the local path."""
|
||||
|
||||
global tarball_path
|
||||
os.makedirs(tarball_path, exist_ok=True)
|
||||
path = os.path.join(tarball_path, os.path.basename(url))
|
||||
|
||||
try:
|
||||
calculated_md5 = file_md5(path)
|
||||
if md5 == calculated_md5: return path
|
||||
os.unlink(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
tmp_path = path + '.tmp'
|
||||
|
||||
print("download", url)
|
||||
urllib.request.urlretrieve(url, tmp_path)
|
||||
calculated_md5 = file_md5(tmp_path)
|
||||
if calculated_md5 != md5:
|
||||
os.unlink(tmp_path)
|
||||
raise "MD5 mismatch"
|
||||
|
||||
os.rename(tmp_path, path)
|
||||
return path
|
||||
|
||||
class Project:
|
||||
def __init__(self, url, md5, installed, name=None, version=None,
|
||||
base=None,
|
||||
use_cxx=False, use_clang=False):
|
||||
if base is None:
|
||||
basename = os.path.basename(url)
|
||||
m = re.match(r'^(.+)\.(tar(\.(gz|bz2|xz|lzma))?|zip)$', basename)
|
||||
if not m: raise
|
||||
self.base = m.group(1)
|
||||
else:
|
||||
self.base = base
|
||||
|
||||
if name is None or version is None:
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?)$', self.base)
|
||||
if name is None: name = m.group(1)
|
||||
if version is None: version = m.group(2)
|
||||
|
||||
self.name = name
|
||||
self.version = version
|
||||
|
||||
self.url = url
|
||||
self.md5 = md5
|
||||
self.installed = installed
|
||||
|
||||
self.use_cxx = use_cxx
|
||||
self.use_clang = use_clang
|
||||
|
||||
def download(self):
|
||||
return download_tarball(self.url, self.md5)
|
||||
|
||||
def is_installed(self):
|
||||
global root_path
|
||||
tarball = self.download()
|
||||
installed = os.path.join(root_path, self.installed)
|
||||
tarball_mtime = os.path.getmtime(tarball)
|
||||
try:
|
||||
return os.path.getmtime(installed) >= tarball_mtime
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
def unpack(self):
|
||||
global src_path
|
||||
tarball = self.download()
|
||||
path = os.path.join(src_path, self.base)
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.makedirs(src_path, exist_ok=True)
|
||||
subprocess.check_call(['/bin/tar', 'xfC', tarball, src_path])
|
||||
return path
|
||||
|
||||
def make_build_path(self):
|
||||
path = os.path.join(build_path, self.base)
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.makedirs(path, exist_ok=True)
|
||||
return path
|
||||
|
||||
class AutotoolsProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
autogen=False,
|
||||
cppflags='',
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
self.autogen = autogen
|
||||
self.cppflags = cppflags
|
||||
|
||||
def build(self):
|
||||
src = self.unpack()
|
||||
if self.autogen:
|
||||
subprocess.check_call(['/usr/bin/aclocal'], cwd=src)
|
||||
subprocess.check_call(['/usr/bin/automake', '--add-missing', '--force-missing', '--foreign'], cwd=src)
|
||||
subprocess.check_call(['/usr/bin/autoconf'], cwd=src)
|
||||
subprocess.check_call(['/usr/bin/libtoolize', '--force'], cwd=src)
|
||||
|
||||
build = self.make_build_path()
|
||||
|
||||
select_toolchain(use_cxx=self.use_cxx, use_clang=self.use_clang)
|
||||
configure = [
|
||||
os.path.join(src, 'configure'),
|
||||
'CC=' + cc,
|
||||
'CXX=' + cxx,
|
||||
'CFLAGS=' + cflags,
|
||||
'CXXFLAGS=' + cxxflags,
|
||||
'CPPFLAGS=' + cppflags + ' ' + self.cppflags,
|
||||
'LDFLAGS=' + ldflags,
|
||||
'LIBS=' + libs,
|
||||
'AR=' + ar,
|
||||
'STRIP=' + strip,
|
||||
'--host=' + host_arch,
|
||||
'--prefix=' + root_path,
|
||||
'--with-sysroot=' + target_root,
|
||||
'--enable-silent-rules',
|
||||
] + self.configure_args
|
||||
|
||||
subprocess.check_call(configure, cwd=build)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], cwd=build)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', 'install'], cwd=build)
|
||||
|
||||
class FfmpegProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
cppflags='',
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
self.cppflags = cppflags
|
||||
|
||||
def build(self):
|
||||
src = self.unpack()
|
||||
build = self.make_build_path()
|
||||
|
||||
select_toolchain(use_cxx=self.use_cxx, use_clang=self.use_clang)
|
||||
configure = [
|
||||
os.path.join(src, 'configure'),
|
||||
'--cc=' + cc,
|
||||
'--cxx=' + cxx,
|
||||
'--extra-cflags=' + cflags + ' ' + cppflags + ' ' + self.cppflags,
|
||||
'--extra-cxxflags=' + cxxflags + ' ' + cppflags + ' ' + self.cppflags,
|
||||
'--extra-ldflags=' + ldflags,
|
||||
'--extra-libs=' + libs,
|
||||
'--ar=' + ar,
|
||||
'--enable-cross-compile',
|
||||
'--target-os=linux',
|
||||
'--arch=' + ndk_arch,
|
||||
'--cpu=cortex-a8',
|
||||
'--prefix=' + root_path,
|
||||
] + self.configure_args
|
||||
|
||||
subprocess.check_call(configure, cwd=build)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], cwd=build)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', 'install'], cwd=build)
|
||||
|
||||
class BoostProject(Project):
|
||||
def __init__(self, url, md5, installed,
|
||||
**kwargs):
|
||||
m = re.match(r'.*/boost_(\d+)_(\d+)_(\d+)\.tar\.bz2$', url)
|
||||
version = "%s.%s.%s" % (m.group(1), m.group(2), m.group(3))
|
||||
Project.__init__(self, url, md5, installed,
|
||||
name='boost', version=version,
|
||||
**kwargs)
|
||||
|
||||
def build(self):
|
||||
src = self.unpack()
|
||||
|
||||
# install the headers manually; don't build any library
|
||||
# (because right now, we only use header-only libraries)
|
||||
includedir = os.path.join(root_path, 'include')
|
||||
for dirpath, dirnames, filenames in os.walk(os.path.join(src, 'boost')):
|
||||
relpath = dirpath[len(src)+1:]
|
||||
destdir = os.path.join(includedir, relpath)
|
||||
try:
|
||||
os.mkdir(destdir)
|
||||
except:
|
||||
pass
|
||||
for name in filenames:
|
||||
if name[-4:] == '.hpp':
|
||||
shutil.copyfile(os.path.join(dirpath, name),
|
||||
os.path.join(destdir, name))
|
||||
|
||||
# a list of third-party libraries to be used by MPD on Android
|
||||
thirdparty_libs = [
|
||||
AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.xz',
|
||||
'5c3a34309d8b98640827e5d0991a4015',
|
||||
'lib/libogg.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
),
|
||||
|
||||
AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.4.tar.xz',
|
||||
'55f2288055e44754275a17c9a2497391',
|
||||
'lib/libvorbis.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
),
|
||||
|
||||
AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz',
|
||||
'c5a8cf7c0b066759542bc4ca46817ac6',
|
||||
'lib/libopus.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
use_clang=True,
|
||||
),
|
||||
|
||||
AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.3.1.tar.xz',
|
||||
'b9922c9a0378c88d3e901b234f852698',
|
||||
'lib/libFLAC.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-xmms-plugin', '--disable-cpplibs',
|
||||
],
|
||||
use_clang=True,
|
||||
),
|
||||
|
||||
AutotoolsProject(
|
||||
'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.1b.tar.gz',
|
||||
'e5808ad997ba32c498803822078748c3',
|
||||
'lib/libid3tag.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
autogen=True,
|
||||
),
|
||||
|
||||
AutotoolsProject(
|
||||
'ftp://ftp.mars.org/pub/mpeg/libmad-0.15.1b.tar.gz',
|
||||
'1be543bc30c56fb6bea1d7bf6a64e66c',
|
||||
'lib/libmad.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
autogen=True,
|
||||
),
|
||||
|
||||
FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-2.5.tar.bz2',
|
||||
'4346fe710cc6bdd981f6534d2420d1ab',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--enable-gpl',
|
||||
'--enable-small',
|
||||
'--disable-pthreads',
|
||||
'--disable-runtime-cpudetect',
|
||||
'--disable-programs',
|
||||
'--disable-doc',
|
||||
'--disable-avdevice',
|
||||
'--disable-swresample',
|
||||
'--disable-swscale',
|
||||
'--disable-postproc',
|
||||
'--disable-avfilter',
|
||||
'--disable-network',
|
||||
'--disable-encoders',
|
||||
'--disable-protocols',
|
||||
'--disable-outdevs',
|
||||
'--disable-filters',
|
||||
],
|
||||
),
|
||||
|
||||
AutotoolsProject(
|
||||
'http://curl.haxx.se/download/curl-7.39.0.tar.lzma',
|
||||
'e9aa6dec29920eba8ef706ea5823bad7',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-debug',
|
||||
'--enable-http',
|
||||
'--enable-ipv6',
|
||||
'--disable-ftp', '--disable-file',
|
||||
'--disable-ldap', '--disable-ldaps',
|
||||
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
|
||||
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
|
||||
'--disable-gopher',
|
||||
'--disable-manual',
|
||||
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
|
||||
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
|
||||
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
|
||||
],
|
||||
use_clang=True,
|
||||
),
|
||||
|
||||
BoostProject(
|
||||
'http://netcologne.dl.sourceforge.net/project/boost/boost/1.55.0/boost_1_55_0.tar.bz2',
|
||||
'd6eef4b4cacb2183f2bf265a5a03a354',
|
||||
'include/boost/version.hpp',
|
||||
),
|
||||
]
|
||||
|
||||
# build the third-party libraries
|
||||
for x in thirdparty_libs:
|
||||
if not x.is_installed():
|
||||
x.build()
|
||||
|
||||
# configure and build MPD
|
||||
select_toolchain(use_cxx=True, use_clang=True)
|
||||
|
||||
configure = [
|
||||
os.path.join(mpd_path, 'configure'),
|
||||
'CC=' + cc,
|
||||
'CXX=' + cxx,
|
||||
'CFLAGS=' + cflags,
|
||||
'CXXFLAGS=' + cxxflags,
|
||||
'CPPFLAGS=' + cppflags,
|
||||
'LDFLAGS=' + ldflags,
|
||||
'LIBS=' + libs,
|
||||
'AR=' + ar,
|
||||
'STRIP=' + strip,
|
||||
'--host=' + host_arch,
|
||||
'--prefix=' + root_path,
|
||||
'--with-sysroot=' + target_root,
|
||||
'--with-android-sdk=' + sdk_path,
|
||||
|
||||
'--enable-silent-rules',
|
||||
|
||||
'--disable-glib',
|
||||
'--disable-icu',
|
||||
|
||||
# disabled for now because these features require GLib:
|
||||
'--disable-httpd-output',
|
||||
'--disable-vorbis-encoder',
|
||||
|
||||
] + configure_args
|
||||
|
||||
subprocess.check_call(configure)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'])
|
||||
11
android/custom_rules.xml
Normal file
11
android/custom_rules.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project name="mpd_rules">
|
||||
<!-- setting these two properties works around a bug in Android
|
||||
SDK's build.xml, which deletes all .class files every time -->
|
||||
<property name="build.last.is.packaging.debug" value="true" />
|
||||
<property name="build.is.packaging.debug" value="true" />
|
||||
|
||||
<target name="compile-jni-classes"
|
||||
depends="-set-debug-mode,-compile"/>
|
||||
</project>
|
||||
5
android/res/values/strings.xml
Normal file
5
android/res/values/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="app_name">MPD</string>
|
||||
</resources>
|
||||
30
android/src/Bridge.java
Normal file
30
android/src/Bridge.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Bridge to native code.
|
||||
*/
|
||||
public class Bridge {
|
||||
public static native void run(Context context);
|
||||
public static native void shutdown();
|
||||
}
|
||||
39
android/src/Loader.java
Normal file
39
android/src/Loader.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class Loader {
|
||||
private static final String TAG = "MPD";
|
||||
|
||||
public static boolean loaded = false;
|
||||
public static String error;
|
||||
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("mpd");
|
||||
loaded = true;
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.e(TAG, e.getMessage());
|
||||
error = e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
75
android/src/Main.java
Normal file
75
android/src/Main.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
package org.musicpd;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.widget.TextView;
|
||||
import android.util.Log;
|
||||
|
||||
public class Main extends Activity implements Runnable {
|
||||
private static final String TAG = "MPD";
|
||||
|
||||
Thread thread;
|
||||
|
||||
TextView textView;
|
||||
|
||||
final Handler quitHandler = new Handler() {
|
||||
public void handleMessage(Message msg) {
|
||||
textView.setText("Music Player Daemon has quit");
|
||||
|
||||
// TODO: what now? restart?
|
||||
}
|
||||
};
|
||||
|
||||
@Override protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!Loader.loaded) {
|
||||
TextView tv = new TextView(this);
|
||||
tv.setText("Failed to load the native MPD libary.\n" +
|
||||
"Report this problem to us, and include the following information:\n" +
|
||||
"ABI=" + Build.CPU_ABI + "\n" +
|
||||
"PRODUCT=" + Build.PRODUCT + "\n" +
|
||||
"FINGERPRINT=" + Build.FINGERPRINT + "\n" +
|
||||
"error=" + Loader.error);
|
||||
setContentView(tv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread == null || !thread.isAlive()) {
|
||||
thread = new Thread(this, "NativeMain");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
textView = new TextView(this);
|
||||
textView.setText("Music Player Daemon is running"
|
||||
+ "\nCAUTION: this version is EXPERIMENTAL!");
|
||||
setContentView(textView);
|
||||
}
|
||||
|
||||
@Override public void run() {
|
||||
Bridge.run(this);
|
||||
quitHandler.sendMessage(quitHandler.obtainMessage());
|
||||
}
|
||||
}
|
||||
140
autogen.sh
140
autogen.sh
@@ -1,137 +1,11 @@
|
||||
#!/bin/sh
|
||||
# Run this to set up the build system: configure, makefiles, etc.
|
||||
# (at one point this was based on the version in enlightenment's cvs)
|
||||
|
||||
package="mpd"
|
||||
set -e
|
||||
|
||||
olddir="`pwd`"
|
||||
srcdir="`dirname $0`"
|
||||
test -z "$srcdir" && srcdir=.
|
||||
cd "$srcdir"
|
||||
DIE=
|
||||
AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]*\).*/\1/"
|
||||
AC_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]\).*/\1/"
|
||||
VERSIONMKINT="sed -e s/[^0-9]//"
|
||||
if test -n "$AM_FORCE_VERSION"
|
||||
then
|
||||
AM_VERSIONS="$AM_FORCE_VERSION"
|
||||
else
|
||||
AM_VERSIONS='1.11'
|
||||
fi
|
||||
if test -n "$AC_FORCE_VERSION"
|
||||
then
|
||||
AC_VERSIONS="$AC_FORCE_VERSION"
|
||||
else
|
||||
AC_VERSIONS='2.60 2.61'
|
||||
fi
|
||||
rm -rf config.cache build
|
||||
mkdir build
|
||||
|
||||
versioned_bins ()
|
||||
{
|
||||
bin="$1"
|
||||
needed_int=`echo $VERNEEDED | $VERSIONMKINT`
|
||||
for i in $VERSIONS
|
||||
do
|
||||
i_int=`echo $i | $VERSIONMKINT`
|
||||
if test $i_int -ge $needed_int
|
||||
then
|
||||
echo $bin-$i $bin$i $bin-$i_int $bin$i_int
|
||||
fi
|
||||
done
|
||||
echo $bin
|
||||
}
|
||||
|
||||
for c in autoconf autoheader automake aclocal
|
||||
do
|
||||
uc=`echo $c | tr '[:lower:]' '[:upper:]'`
|
||||
eval "val=`echo '$'$uc`"
|
||||
if test -n "$val"
|
||||
then
|
||||
echo "$uc=$val in environment, will not attempt to auto-detect"
|
||||
continue
|
||||
fi
|
||||
|
||||
case "$c" in
|
||||
autoconf|autoheader)
|
||||
VERNEEDED=`fgrep AC_PREREQ configure.ac | $AC_VERSIONGREP`
|
||||
VERSIONS="$AC_VERSIONS"
|
||||
pkg=autoconf
|
||||
;;
|
||||
automake|aclocal)
|
||||
VERNEEDED=`fgrep AUTOMAKE_OPTIONS Makefile.am | $AM_VERSIONGREP`
|
||||
VERSIONS="$AM_VERSIONS"
|
||||
pkg=automake
|
||||
;;
|
||||
esac
|
||||
printf "checking for $c ... "
|
||||
for x in `versioned_bins $c`; do
|
||||
($x --version < /dev/null > /dev/null 2>&1) > /dev/null 2>&1
|
||||
if test $? -eq 0
|
||||
then
|
||||
echo $x
|
||||
eval $uc=$x
|
||||
break
|
||||
fi
|
||||
done
|
||||
eval "val=`echo '$'$uc`"
|
||||
if test -z "$val"
|
||||
then
|
||||
if test $c = $pkg
|
||||
then
|
||||
DIE="$DIE $c=$VERNEEDED"
|
||||
else
|
||||
DIE="$DIE $c($pkg)=$VERNEEDED"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if test -n "$DIE"
|
||||
then
|
||||
echo "You must have the following installed to compile $package:"
|
||||
for i in $DIE
|
||||
do
|
||||
printf ' '
|
||||
echo $i | sed -e 's/(/ (from /' -e 's/=\(.*\)/ (>= \1)/'
|
||||
done
|
||||
echo "Download the appropriate package(s) for your system,"
|
||||
echo "or get the source from one of the GNU ftp sites"
|
||||
echo "listed in http://www.gnu.org/order/ftp.html"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating configuration files for $package, please wait...."
|
||||
|
||||
ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I m4"
|
||||
|
||||
# /usr/share/aclocal is most likely included by default, already...
|
||||
ac_local_paths='
|
||||
/usr/local/share/aclocal
|
||||
/sw/share/aclocal
|
||||
/usr/pkg/share/aclocal
|
||||
/opt/share/aclocal
|
||||
/usr/gnu/share/aclocal
|
||||
'
|
||||
|
||||
for i in $ac_local_paths; do
|
||||
if test -d "$i"; then
|
||||
ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I $i"
|
||||
# we probably only want one of these...
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo " $ACLOCAL $ACLOCAL_FLAGS"
|
||||
$ACLOCAL $ACLOCAL_FLAGS || exit 1
|
||||
|
||||
echo " $AUTOHEADER"
|
||||
$AUTOHEADER || exit 1
|
||||
|
||||
echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS"
|
||||
$AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1
|
||||
|
||||
echo " $AUTOCONF"
|
||||
$AUTOCONF || exit 1
|
||||
|
||||
cd "$olddir"
|
||||
if test x$NOCONFIGURE = x; then
|
||||
"$srcdir"/configure "$@" || exit 1
|
||||
fi
|
||||
aclocal -I m4 $ACLOCAL_FLAGS
|
||||
autoheader
|
||||
automake --add-missing $AUTOMAKE_FLAGS
|
||||
autoconf
|
||||
|
||||
486
configure.ac
486
configure.ac
@@ -1,20 +1,25 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.18.13, mpd-devel@musicpd.org)
|
||||
AC_INIT(mpd, 0.19.15, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=18
|
||||
VERSION_REVISION=0
|
||||
VERSION_MINOR=19
|
||||
VERSION_REVISION=15
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||
AC_CONFIG_AUX_DIR(build)
|
||||
AM_INIT_AUTOMAKE([foreign 1.11 dist-xz subdir-objects])
|
||||
AM_SILENT_RULES
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.18.0", [The MPD protocol version])
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.19.0", [The MPD protocol version])
|
||||
|
||||
GIT_COMMIT=`GIT_DIR="$srcdir/.git" git describe --dirty --always 2>/dev/null`
|
||||
if test x$GIT_COMMIT != x; then
|
||||
AC_DEFINE_UNQUOTED(GIT_COMMIT, ["$GIT_COMMIT"], [The current git commit])
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Programs
|
||||
@@ -65,9 +70,31 @@ dnl OS Specific Defaults
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
host_is_unix=yes
|
||||
host_is_linux=no
|
||||
host_is_android=no
|
||||
host_is_darwin=no
|
||||
host_is_solaris=no
|
||||
host_is_windows=no
|
||||
|
||||
linux_auto=no
|
||||
|
||||
case "$host_os" in
|
||||
linux-android*)
|
||||
host_is_android=yes
|
||||
host_is_linux=yes
|
||||
linux_auto=auto
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DANDROID"
|
||||
;;
|
||||
|
||||
linux*)
|
||||
host_is_linux=yes
|
||||
linux_auto=auto
|
||||
|
||||
dnl allow using all glibc features
|
||||
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
|
||||
;;
|
||||
|
||||
mingw32* | windows*)
|
||||
AC_CONFIG_FILES([
|
||||
src/win32/mpd_win32_rc.rc
|
||||
@@ -76,14 +103,21 @@ mingw32* | windows*)
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DWIN32_LEAN_AND_MEAN"
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0600 -D_WIN32_WINNT=0x0600"
|
||||
LIBS="$LIBS -lws2_32"
|
||||
HAVE_WINDOWS=1
|
||||
host_is_windows=yes
|
||||
host_is_unix=no
|
||||
;;
|
||||
|
||||
darwin*)
|
||||
host_is_darwin=yes
|
||||
;;
|
||||
|
||||
solaris*)
|
||||
host_is_solaris=yes
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL([HAVE_WINDOWS], [test x$HAVE_WINDOWS = x1])
|
||||
|
||||
AM_CONDITIONAL([ANDROID], [test x$host_is_android = xyes])
|
||||
AM_CONDITIONAL([HAVE_WINDOWS], [test x$host_is_windows = xyes])
|
||||
|
||||
if test -z "$prefix" || test "x$prefix" = xNONE; then
|
||||
local_lib=
|
||||
@@ -121,6 +155,27 @@ if test -z "$prefix" || test "x$prefix" = xNONE; then
|
||||
done
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Android
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AC_ARG_WITH([android-sdk],
|
||||
AS_HELP_STRING([--with-android-sdk=DIR],
|
||||
[Directory for Android SDK]),
|
||||
[], [with_android_sdk=no])
|
||||
|
||||
if test x$host_is_android = xyes; then
|
||||
if test x$with_android_sdk = xno; then
|
||||
AC_MSG_ERROR([Android build requires option --with-android-sdk=DIR])
|
||||
fi
|
||||
|
||||
if ! test -x $with_android_sdk/tools/android; then
|
||||
AC_MSG_ERROR([Android SDK not found in $with_android_sdk])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(ANDROID_SDK, [$with_android_sdk])
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Language Checks
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -137,7 +192,8 @@ fi
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Header/Library Checks
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_CHECK_FUNCS(daemon fork)
|
||||
|
||||
AC_SEARCH_LIBS([clock_gettime], [rt])
|
||||
|
||||
AC_SEARCH_LIBS([syslog], [bsd socket inet],
|
||||
[AC_DEFINE(HAVE_SYSLOG, 1, [Define if syslog() is available])])
|
||||
@@ -145,10 +201,18 @@ AC_SEARCH_LIBS([syslog], [bsd socket inet],
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
AC_SEARCH_LIBS([gethostbyname], [nsl])
|
||||
|
||||
if test x$host_is_linux = xyes; then
|
||||
AC_CHECK_FUNCS(pipe2 accept4)
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
|
||||
AC_CHECK_FUNCS(initgroups)
|
||||
AC_CHECK_FUNCS(strndup)
|
||||
|
||||
if test x$host_is_linux = xyes; then
|
||||
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)
|
||||
MPD_OPTIONAL_FUNC(signalfd, signalfd, USE_SIGNALFD)
|
||||
MPD_OPTIONAL_FUNC(epoll, epoll_create1, USE_EPOLL)
|
||||
fi
|
||||
|
||||
AC_SEARCH_LIBS([exp], [m],,
|
||||
[AC_MSG_ERROR([exp() not found])])
|
||||
@@ -156,14 +220,96 @@ AC_SEARCH_LIBS([exp], [m],,
|
||||
AC_CHECK_HEADERS(locale.h)
|
||||
AC_CHECK_HEADERS(valgrind/memcheck.h)
|
||||
|
||||
AC_CHECK_HEADERS([sys/prctl.h], AC_CHECK_FUNCS([prctl]))
|
||||
|
||||
AX_PTHREAD
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
|
||||
AM_CXXFLAGS="$AM_CXXFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
AC_CHECK_LIB([pthread], [pthread_setname_np],
|
||||
[have_pthread_setname_np=yes],
|
||||
[have_pthread_setname_np=no])
|
||||
if test x$have_pthread_setname_np = xyes; then
|
||||
AC_DEFINE(HAVE_PTHREAD_SETNAME_NP, 1, [Is pthread_setname_np() available?])
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Event loop selection
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
MPD_OPTIONAL_FUNC_NODEF(poll, poll)
|
||||
|
||||
if test x$host_is_linux = xyes; then
|
||||
MPD_OPTIONAL_FUNC_NODEF(epoll, epoll_create1)
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(pollmethod,
|
||||
AS_HELP_STRING(
|
||||
[--with-pollmethod=@<:@epoll|poll|winselect|auto@:>@],
|
||||
[specify poll method for internal event loop (default=auto)]),,
|
||||
[with_pollmethod=auto])
|
||||
|
||||
if test "x$with_pollmethod" = xauto; then
|
||||
if test "x$enable_epoll" = xyes; then
|
||||
with_pollmethod=epoll
|
||||
elif test "x$enable_poll" = xyes; then
|
||||
with_pollmethod=poll
|
||||
elif test "x$host_is_windows" = xyes; then
|
||||
with_pollmethod=winselect
|
||||
else
|
||||
AC_MSG_ERROR([no poll method is available for your platform])
|
||||
fi
|
||||
fi
|
||||
case "$with_pollmethod" in
|
||||
epoll)
|
||||
AC_DEFINE(USE_EPOLL, 1, [Define to poll sockets with epoll])
|
||||
;;
|
||||
poll)
|
||||
AC_DEFINE(USE_POLL, 1, [Define to poll sockets with poll])
|
||||
;;
|
||||
winselect)
|
||||
AC_DEFINE(USE_WINSELECT, 1,
|
||||
[Define to poll sockets with Windows select])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([unknown pollmethod option: $with_pollmethod])
|
||||
esac
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Allow tools to be specifically built
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AC_ARG_ENABLE(database,
|
||||
AS_HELP_STRING([--enable-database],
|
||||
[enable support for the music database]),,
|
||||
enable_database=yes)
|
||||
AM_CONDITIONAL(ENABLE_DATABASE, test x$enable_database = xyes)
|
||||
if test x$enable_database = xyes; then
|
||||
database_auto=auto
|
||||
AC_DEFINE(ENABLE_DATABASE, 1, [Define to enable the music database])
|
||||
else
|
||||
database_auto=no
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(libmpdclient,
|
||||
AS_HELP_STRING([--enable-libmpdclient],
|
||||
[enable support for the MPD client]),,
|
||||
enable_libmpdclient=auto)
|
||||
MPD_DEPENDS([enable_libmpdclient], [enable_database],
|
||||
[Cannot use --enable-libmpdclient with --disable-database])
|
||||
|
||||
AC_ARG_ENABLE(expat,
|
||||
AS_HELP_STRING([--enable-expat],
|
||||
[enable the expat XML parser]),,
|
||||
enable_expat=auto)
|
||||
|
||||
AC_ARG_ENABLE(upnp,
|
||||
AS_HELP_STRING([--enable-upnp],
|
||||
[enable UPnP client support (default: auto)]),,
|
||||
enable_upnp=auto)
|
||||
MPD_DEPENDS([enable_upnp], [enable_database],
|
||||
[Cannot use --enable-upnp with --disable-database])
|
||||
|
||||
AC_ARG_ENABLE(adplug,
|
||||
AS_HELP_STRING([--enable-adplug],
|
||||
@@ -172,7 +318,7 @@ AC_ARG_ENABLE(adplug,
|
||||
|
||||
AC_ARG_ENABLE(alsa,
|
||||
AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
|
||||
[enable_alsa=auto])
|
||||
[enable_alsa=$linux_auto])
|
||||
|
||||
AC_ARG_ENABLE(roar,
|
||||
AS_HELP_STRING([--enable-roar],
|
||||
@@ -183,12 +329,19 @@ AC_ARG_ENABLE(ao,
|
||||
AS_HELP_STRING([--enable-ao],
|
||||
[enable support for libao]),,
|
||||
enable_ao=auto)
|
||||
MPD_DEPENDS([enable_ao], [enable_glib],
|
||||
[Cannot use --enable-ao with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(audiofile,
|
||||
AS_HELP_STRING([--enable-audiofile],
|
||||
[enable audiofile support (WAV and others)]),,
|
||||
enable_audiofile=auto)
|
||||
|
||||
AC_ARG_ENABLE(zlib,
|
||||
AS_HELP_STRING([--enable-zlib],
|
||||
[enable zlib support (default: auto)]),,
|
||||
enable_zlib=auto)
|
||||
|
||||
AC_ARG_ENABLE(bzip2,
|
||||
AS_HELP_STRING([--enable-bzip2],
|
||||
[enable bzip2 archive support (default: auto)]),,
|
||||
@@ -198,12 +351,24 @@ AC_ARG_ENABLE(cdio-paranoia,
|
||||
AS_HELP_STRING([--enable-cdio-paranoia],
|
||||
[enable support for audio CD support]),,
|
||||
enable_cdio_paranoia=auto)
|
||||
MPD_DEPENDS([enable_cdio_paranoia], [enable_glib],
|
||||
[Cannot use --enable-cdio-paranoia with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(curl,
|
||||
AS_HELP_STRING([--enable-curl],
|
||||
[enable support for libcurl HTTP streaming (default: auto)]),,
|
||||
[enable_curl=auto])
|
||||
|
||||
AC_ARG_ENABLE(smbclient,
|
||||
AS_HELP_STRING([--enable-smbclient],
|
||||
[enable support for libsmbclient (default: auto)]),,
|
||||
[enable_smbclient=auto])
|
||||
|
||||
AC_ARG_ENABLE(nfs,
|
||||
AS_HELP_STRING([--enable-nfs],
|
||||
[enable support for libnfs (default: auto)]),,
|
||||
[enable_nfs=auto])
|
||||
|
||||
AC_ARG_ENABLE(debug,
|
||||
AS_HELP_STRING([--enable-debug],
|
||||
[enable debugging (default: disabled)]),,
|
||||
@@ -214,6 +379,11 @@ AC_ARG_ENABLE(documentation,
|
||||
[build documentation (default: disable)]),,
|
||||
[enable_documentation=no])
|
||||
|
||||
AC_ARG_ENABLE(dsd,
|
||||
AS_HELP_STRING([--enable-dsd],
|
||||
[enable DSD decoder (default: enable)]),,
|
||||
[enable_dsd=yes])
|
||||
|
||||
AC_ARG_ENABLE(ffmpeg,
|
||||
AS_HELP_STRING([--enable-ffmpeg],
|
||||
[enable FFMPEG support]),,
|
||||
@@ -238,11 +408,15 @@ AC_ARG_ENABLE(gme,
|
||||
AS_HELP_STRING([--enable-gme],
|
||||
[enable Blargg's game music emulator plugin]),,
|
||||
enable_gme=auto)
|
||||
MPD_DEPENDS([enable_gme], [enable_glib],
|
||||
[Cannot use --enable-gme with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(httpd-output,
|
||||
AS_HELP_STRING([--enable-httpd-output],
|
||||
[enables the HTTP server output]),,
|
||||
[enable_httpd_output=auto])
|
||||
MPD_DEPENDS([enable_httpd_output], [enable_glib],
|
||||
[Cannot use --enable-httpd-output with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(id3,
|
||||
AS_HELP_STRING([--enable-id3],
|
||||
@@ -268,18 +442,17 @@ AC_ARG_ENABLE(jack,
|
||||
AS_HELP_STRING([--enable-jack],
|
||||
[enable jack support]),,
|
||||
enable_jack=auto)
|
||||
MPD_DEPENDS([enable_jack], [enable_glib],
|
||||
[Cannot use --enable-jack with --disable-glib])
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
AC_ARG_ENABLE(despotify,
|
||||
AS_HELP_STRING([--enable-despotify],
|
||||
[enable support for despotify (default: disable)]),,
|
||||
[enable_despotify=no])
|
||||
|
||||
AC_ARG_ENABLE(soundcloud,
|
||||
AS_HELP_STRING([--enable-soundcloud],
|
||||
[enable support for soundcloud.com]),,
|
||||
[enable_soundcloud=auto])
|
||||
MPD_DEPENDS([enable_soundcloud], [enable_glib],
|
||||
[Cannot use --enable-soundcloud with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(lame-encoder,
|
||||
AS_HELP_STRING([--enable-lame-encoder],
|
||||
@@ -295,6 +468,11 @@ AC_ARG_ENABLE(lsr,
|
||||
[enable libsamplerate support]),,
|
||||
enable_lsr=auto)
|
||||
|
||||
AC_ARG_ENABLE(soxr,
|
||||
AS_HELP_STRING([--enable-soxr],
|
||||
[enable the libsoxr resampler]),,
|
||||
enable_soxr=auto)
|
||||
|
||||
AC_ARG_ENABLE(mad,
|
||||
AS_HELP_STRING([--enable-mad],
|
||||
[enable libmad mp3 decoder plugin]),,
|
||||
@@ -364,7 +542,13 @@ AC_ARG_ENABLE(sidplay,
|
||||
AS_HELP_STRING([--enable-sidplay],
|
||||
[enable C64 SID support via libsidplay2]),,
|
||||
enable_sidplay=auto)
|
||||
MPD_DEPENDS([enable_sidplay], [enable_glib],
|
||||
[Cannot use --enable-sidplay with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(shine-encoder,
|
||||
AS_HELP_STRING([--enable-shine-encoder],
|
||||
[enables shine encoder]),,
|
||||
[enable_shine_encoder=auto])
|
||||
|
||||
AC_ARG_ENABLE(shout,
|
||||
AS_HELP_STRING([--enable-shout],
|
||||
@@ -379,17 +563,19 @@ AC_ARG_ENABLE(sndfile,
|
||||
AC_ARG_ENABLE(solaris_output,
|
||||
AS_HELP_STRING([--enable-solaris-output],
|
||||
[enables the Solaris /dev/audio output]),,
|
||||
[enable_solaris_output=auto])
|
||||
[enable_solaris_output=$host_is_solaris])
|
||||
|
||||
AC_ARG_ENABLE(sqlite,
|
||||
AS_HELP_STRING([--enable-sqlite],
|
||||
[enable support for the SQLite database]),,
|
||||
[enable_sqlite=auto])
|
||||
[enable_sqlite=$database_auto])
|
||||
MPD_DEPENDS([enable_sqlite], [enable_glib],
|
||||
[Cannot use --enable-sqlite with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(systemd-daemon,
|
||||
AS_HELP_STRING([--enable-systemd-daemon],
|
||||
[use the systemd daemon library (default=auto)]),,
|
||||
[enable_systemd_daemon=auto])
|
||||
[enable_systemd_daemon=$linux_auto])
|
||||
|
||||
AC_ARG_ENABLE(tcp,
|
||||
AS_HELP_STRING([--disable-tcp],
|
||||
@@ -414,7 +600,7 @@ AC_ARG_ENABLE(twolame-encoder,
|
||||
AC_ARG_ENABLE(un,
|
||||
AS_HELP_STRING([--disable-un],
|
||||
[disable support for clients connecting via unix domain sockets (default: enable)]),,
|
||||
[enable_un=yes])
|
||||
[enable_un=$host_is_unix])
|
||||
|
||||
AC_ARG_ENABLE(vorbis,
|
||||
AS_HELP_STRING([--enable-vorbis],
|
||||
@@ -425,6 +611,8 @@ AC_ARG_ENABLE(vorbis-encoder,
|
||||
AS_HELP_STRING([--enable-vorbis-encoder],
|
||||
[enable the Ogg Vorbis encoder]),,
|
||||
[enable_vorbis_encoder=auto])
|
||||
MPD_DEPENDS([enable_vorbis_encoder], [enable_glib],
|
||||
[Cannot use --enable-vorbis-encoder with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(wave-encoder,
|
||||
AS_HELP_STRING([--enable-wave-encoder],
|
||||
@@ -435,6 +623,8 @@ AC_ARG_ENABLE(wavpack,
|
||||
AS_HELP_STRING([--enable-wavpack],
|
||||
[enable WavPack support]),,
|
||||
enable_wavpack=auto)
|
||||
MPD_DEPENDS([enable_wavpack], [enable_glib],
|
||||
[Cannot use --enable-wavpack with --disable-glib])
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
AS_HELP_STRING([--enable-werror],
|
||||
@@ -470,6 +660,47 @@ AC_ARG_WITH(tremor-includes,
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Mandatory Libraries
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
no_exceptions=yes
|
||||
|
||||
AX_BOOST_BASE([1.46],, [AC_MSG_ERROR([Boost not found])])
|
||||
|
||||
dnl Don't disable exceptions on Boost older than 1.54, because
|
||||
dnl Boost.Intrusive supports this compiler mode only since 1.54;
|
||||
dnl see https://svn.boost.org/trac/boost/ticket/7849
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
@%:@include <boost/version.hpp>
|
||||
]], [[
|
||||
#if BOOST_VERSION < 105400
|
||||
#error detected Boost older than 1.54
|
||||
#endif
|
||||
]])],, [no_exceptions=no])
|
||||
AC_LANG_POP([C++])
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
|
||||
AC_ARG_ENABLE(icu,
|
||||
AS_HELP_STRING([--enable-icu],
|
||||
[enable libicu for Unicode (default: enabled)]),,
|
||||
enable_icu=yes)
|
||||
|
||||
if test x$enable_icu = xyes; then
|
||||
PKG_CHECK_MODULES([ICU], [icu-i18n],,
|
||||
[AC_MSG_ERROR([libicu not found])])
|
||||
|
||||
AC_DEFINE(HAVE_ICU, 1, [Define if libicu is used])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_ICU, test x$enable_icu = xyes)
|
||||
|
||||
AC_ARG_ENABLE(glib,
|
||||
AS_HELP_STRING([--enable-glib],
|
||||
[enable GLib usage (default: enabled)]),,
|
||||
enable_glib=yes)
|
||||
|
||||
if test x$enable_glib = xyes; then
|
||||
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28 gthread-2.0],,
|
||||
[AC_MSG_ERROR([GLib 2.28 is required])])
|
||||
|
||||
@@ -478,6 +709,10 @@ if test x$GCC = xyes; then
|
||||
GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'`
|
||||
fi
|
||||
|
||||
AC_DEFINE(HAVE_GLIB, 1, [Define if GLib is used])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_GLIB, test x$enable_glib = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Protocol Options
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -514,12 +749,6 @@ if test x$enable_tcp = xyes; then
|
||||
AC_DEFINE(HAVE_TCP, 1, [Define if TCP socket support is enabled])
|
||||
fi
|
||||
|
||||
case "$host_os" in
|
||||
mingw* | windows* | cygwin*)
|
||||
enable_un=no
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x$enable_un = xyes; then
|
||||
AC_DEFINE(HAVE_UN, 1, [Define if unix domain socket support is enabled])
|
||||
STRUCT_UCRED
|
||||
@@ -560,6 +789,15 @@ fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_LIBMPDCLIENT, test x$enable_libmpdclient = xyes)
|
||||
|
||||
dnl -------------------------------- expat --------------------------------
|
||||
MPD_AUTO_PKG(expat, EXPAT, [expat],
|
||||
[expat XML parser], [expat not found])
|
||||
if test x$enable_expat = xyes; then
|
||||
AC_DEFINE(HAVE_EXPAT, 1, [Define to use the expat XML parser])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_EXPAT, test x$enable_expat = xyes)
|
||||
|
||||
dnl --------------------------------- inotify ---------------------------------
|
||||
AC_CHECK_FUNCS(inotify_init inotify_init1)
|
||||
|
||||
@@ -639,7 +877,7 @@ avahi)
|
||||
;;
|
||||
esac
|
||||
|
||||
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client],
|
||||
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client dbus-1],
|
||||
[avahi client library], [avahi-client not found])
|
||||
if test x$enable_avahi = xyes; then
|
||||
AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])
|
||||
@@ -692,21 +930,22 @@ dnl Converter Plugins
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
dnl ------------------------------ libsamplerate ------------------------------
|
||||
MPD_AUTO_PKG(lsr, SAMPLERATE, [samplerate >= 0.0.15],
|
||||
MPD_AUTO_PKG(lsr, SAMPLERATE, [samplerate >= 0.1.3],
|
||||
[libsamplerate resampling], [libsamplerate not found])
|
||||
if test x$enable_lsr = xyes; then
|
||||
AC_DEFINE([HAVE_LIBSAMPLERATE], 1,
|
||||
[Define to enable libsamplerate])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_LIBSAMPLERATE, test x$enable_lsr = xyes)
|
||||
|
||||
if test x$enable_lsr = xyes; then
|
||||
PKG_CHECK_MODULES([SAMPLERATE_013],
|
||||
[samplerate >= 0.1.3],,
|
||||
[AC_DEFINE([HAVE_LIBSAMPLERATE_NOINT], 1,
|
||||
[libsamplerate doesn't provide src_int_to_float_array() (<0.1.3)])])
|
||||
dnl ------------------------------ libsoxr ------------------------------------
|
||||
MPD_AUTO_PKG(soxr, SOXR, [soxr],
|
||||
[libsoxr resampler], [libsoxr not found])
|
||||
if test x$enable_soxr = xyes; then
|
||||
AC_DEFINE([HAVE_SOXR], 1, [Define to enable libsoxr])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_LIBSAMPLERATE, test x$enable_lsr = xyes)
|
||||
AM_CONDITIONAL(HAVE_SOXR, test x$enable_soxr = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Input Plugins
|
||||
@@ -720,13 +959,22 @@ if test x$enable_curl = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_CURL, test x$enable_curl = xyes)
|
||||
|
||||
dnl --------------------------------- Despotify ---------------------------------
|
||||
MPD_AUTO_PKG(despotify, DESPOTIFY, [despotify],
|
||||
[Despotify support], [despotify not found])
|
||||
if test x$enable_despotify = xyes; then
|
||||
AC_DEFINE(ENABLE_DESPOTIFY, 1, [Define when despotify is enabled])
|
||||
dnl ----------------------------------- smbclient -----------------------------
|
||||
MPD_AUTO_PKG_LIB(smbclient, SMBCLIENT, [smbclient >= 0.2],
|
||||
[smbclient], [smbc_init], [-lsmbclient], [],
|
||||
[smbclient input plugin], [libsmbclient not found])
|
||||
if test x$enable_smbclient = xyes; then
|
||||
AC_DEFINE(ENABLE_SMBCLIENT, 1, [Define when libsmbclient is used])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_DESPOTIFY, test x$enable_despotify = xyes)
|
||||
AM_CONDITIONAL(ENABLE_SMBCLIENT, test x$enable_smbclient = xyes)
|
||||
|
||||
dnl ----------------------------------- NFS -----------------------------
|
||||
MPD_AUTO_PKG(nfs, NFS, [libnfs],
|
||||
[NFS input plugin], [libnfs not found])
|
||||
if test x$enable_nfs = xyes; then
|
||||
AC_DEFINE(ENABLE_NFS, 1, [Define when libnfs is used])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_NFS, test x$enable_nfs = xyes)
|
||||
|
||||
dnl --------------------------------- Soundcloud ------------------------------
|
||||
if test x$enable_soundcloud != xno; then
|
||||
@@ -763,6 +1011,30 @@ if test x$enable_mms = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_MMS, test x$enable_mms = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Neighbor Plugins
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AC_ARG_ENABLE(neighbor-plugins,
|
||||
AS_HELP_STRING([--enable-neighbor-plugins],
|
||||
[enable support for neighbor discovery (default: auto)]),,
|
||||
[enable_neighbor_plugins=auto])
|
||||
|
||||
if test x$enable_neighbor_plugins = xauto; then
|
||||
if test x$enable_smbclient = xyes; then
|
||||
enable_neighbor_plugins=yes
|
||||
fi
|
||||
if test x$enable_upnp = xyes; then
|
||||
enable_neighbor_plugins=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$enable_neighbor_plugins = xyes; then
|
||||
AC_DEFINE(ENABLE_NEIGHBOR_PLUGINS, 1,
|
||||
[Define to enable support for neighbor discovery])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_NEIGHBOR_PLUGINS, test x$enable_neighbor_plugins = xyes)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Archive Plugins
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -782,6 +1054,16 @@ fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_ISO9660_TEST, test x$MKISOFS != xno)
|
||||
|
||||
dnl ---------------------------------- zlib ---------------------------------
|
||||
|
||||
MPD_AUTO_PKG(zlib, ZLIB, [zlib],
|
||||
[zlib support], [zlib not found])
|
||||
|
||||
AM_CONDITIONAL(HAVE_ZLIB, test x$enable_zlib = xyes)
|
||||
if test x$enable_zlib = xyes; then
|
||||
AC_DEFINE(HAVE_ZLIB, 1, [Define to enable zlib support])
|
||||
fi
|
||||
|
||||
dnl ---------------------------------- libbz2 ---------------------------------
|
||||
|
||||
MPD_AUTO_LIB(bzip2, BZ2, bz2, BZ2_bzDecompressInit, [-lbz2], [],
|
||||
@@ -798,6 +1080,24 @@ fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_BZIP2_TEST, test x$BZIP2 != xno)
|
||||
|
||||
dnl ---------------------------------- libupnp ---------------------------------
|
||||
|
||||
if test x$enable_expat = xno; then
|
||||
if test x$enable_upnp = xauto; then
|
||||
AC_MSG_WARN([expat disabled -- disabling UPnP])
|
||||
enable_upnp=no
|
||||
elif test x$enable_upnp = xyes; then
|
||||
AC_MSG_ERROR([expat disabled -- required for UPnP])
|
||||
fi
|
||||
fi
|
||||
|
||||
MPD_AUTO_PKG(upnp, UPNP, [libupnp],
|
||||
[UPnP client support], [libupnp not found])
|
||||
if test x$enable_upnp = xyes; then
|
||||
AC_DEFINE(HAVE_LIBUPNP, 1, [Define when libupnp is used])
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_LIBUPNP, test x$enable_upnp = xyes)
|
||||
|
||||
dnl --------------------------------- libzzip ---------------------------------
|
||||
MPD_AUTO_PKG(zzip, ZZIP, [zziplib >= 0.13],
|
||||
[libzzip archive library], [libzzip not found])
|
||||
@@ -846,6 +1146,14 @@ if test x$enable_audiofile = xyes; then
|
||||
AC_DEFINE(HAVE_AUDIOFILE, 1, [Define for audiofile support])
|
||||
fi
|
||||
|
||||
dnl ----------------------------------- DSD -----------------------------------
|
||||
|
||||
if test x$enable_dsd = xyes; then
|
||||
AC_DEFINE(ENABLE_DSD, 1, [Define for the DSD decoder])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_DSD, test x$enable_dsd = xyes)
|
||||
|
||||
dnl ----------------------------------- FAAD ----------------------------------
|
||||
AM_PATH_FAAD()
|
||||
|
||||
@@ -1081,27 +1389,6 @@ AM_CONDITIONAL(ENABLE_WILDMIDI, test x$enable_wildmidi = xyes)
|
||||
|
||||
dnl ------------------------ Post Decoder Plugins Tests -----------------------
|
||||
|
||||
if
|
||||
test x$enable_aac = xno &&
|
||||
test x$enable_audiofile = xno &&
|
||||
test x$enable_ffmpeg = xno &&
|
||||
test x$enable_flac = xno &&
|
||||
test x$enable_fluidsynth = xno &&
|
||||
test x$enable_mad = xno &&
|
||||
test x$enable_mikmod = xno; then
|
||||
test x$enable_modplug = xno &&
|
||||
test x$enable_mpc = xno &&
|
||||
test x$enable_mpg123 = xno &&
|
||||
test x$enable_opus = xno &&
|
||||
test x$enable_sidplay = xno &&
|
||||
test x$enable_tremor = xno &&
|
||||
test x$enable_vorbis = xno &&
|
||||
test x$enable_wavpack = xno &&
|
||||
test x$enable_wildmidi = xno &&
|
||||
|
||||
AC_MSG_ERROR([No input plugins supported!])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_XIPH,
|
||||
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes || test x$enable_opus = xyes)
|
||||
|
||||
@@ -1127,6 +1414,7 @@ else
|
||||
enable_vorbis_encoder=no
|
||||
enable_lame_encoder=no
|
||||
enable_twolame_encoder=no
|
||||
enable_shine_encoder=no
|
||||
enable_wave_encoder=no
|
||||
enable_flac_encoder=no
|
||||
fi
|
||||
@@ -1138,6 +1426,17 @@ if test x$enable_flac_encoder = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_FLAC_ENCODER, test x$enable_flac_encoder = xyes)
|
||||
|
||||
dnl ------------------------------- Shine Encoder ------------------------------
|
||||
|
||||
MPD_AUTO_PKG(shine_encoder, SHINE, [shine >= 3.1],
|
||||
[shine encoder], [libshine not found])
|
||||
|
||||
if test x$enable_shine_encoder = xyes; then
|
||||
AC_DEFINE(ENABLE_SHINE_ENCODER, 1,
|
||||
[Define to enable the shine encoder plugin])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SHINE_ENCODER, test x$enable_shine_encoder = xyes)
|
||||
|
||||
dnl ---------------------------- Ogg Vorbis Encoder ---------------------------
|
||||
MPD_AUTO_PKG(vorbis_encoder, VORBISENC, [vorbisenc vorbis ogg],
|
||||
[Ogg Vorbis encoder], [libvorbisenc not found])
|
||||
@@ -1181,6 +1480,7 @@ if test x$enable_vorbis_encoder != xno ||
|
||||
test x$enable_lame_encoder != xno ||
|
||||
test x$enable_twolame_encoder != xno ||
|
||||
test x$enable_flac_encoder != xno ||
|
||||
test x$enable_shine_encoder != xno ||
|
||||
test x$enable_wave_encoder != xno; then
|
||||
# at least one encoder plugin is enabled
|
||||
enable_encoder=yes
|
||||
@@ -1376,18 +1676,6 @@ AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes)
|
||||
|
||||
dnl --------------------------------- Solaris ---------------------------------
|
||||
|
||||
if test x$enable_solaris_output = xauto; then
|
||||
case "$host_os" in
|
||||
solaris*)
|
||||
enable_solaris_output=yes
|
||||
;;
|
||||
|
||||
*)
|
||||
enable_solaris_output=no
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x$enable_solaris_output = xyes; then
|
||||
AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support])
|
||||
fi
|
||||
@@ -1396,48 +1684,26 @@ AM_CONDITIONAL(ENABLE_SOLARIS_OUTPUT, test x$enable_solaris_output = xyes)
|
||||
|
||||
dnl --------------------------------- WinMM ---------------------------------
|
||||
|
||||
case "$host_os" in
|
||||
mingw32* | windows*)
|
||||
if test "x$host_is_windows" = xyes; then
|
||||
AC_DEFINE(ENABLE_WINMM_OUTPUT, 1, [Define to enable WinMM support])
|
||||
enable_winmm_output=yes
|
||||
LIBS="$LIBS -lwinmm"
|
||||
;;
|
||||
|
||||
*)
|
||||
else
|
||||
enable_winmm_output=no
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_WINMM_OUTPUT, test x$enable_winmm_output = xyes)
|
||||
|
||||
dnl --------------------- Post Audio Output Plugins Tests ---------------------
|
||||
if
|
||||
test x$enable_alsa = xno &&
|
||||
test x$enable_roar = xno &&
|
||||
test x$enable_ao = xno &&
|
||||
test x$enable_fifo = xno &&
|
||||
test x$enable_httpd_output = xno &&
|
||||
test x$enable_jack = xno &&
|
||||
test x$enable_openal = xno &&
|
||||
test x$enable_oss = xno &&
|
||||
test x$enable_osx = xno &&
|
||||
test x$enable_pipe_output = xno &&
|
||||
test x$enable_pulse = xno &&
|
||||
test x$enable_recorder_output = xno &&
|
||||
test x$enable_shout = xno &&
|
||||
test x$enable_solaris_output = xno &&
|
||||
test x$enable_winmm_output = xno; then
|
||||
|
||||
AC_MSG_ERROR([No Audio Output types configured!])
|
||||
fi
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Documentation
|
||||
dnl ---------------------------------------------------------------------------
|
||||
if test x$enable_documentation = xyes; then
|
||||
AC_PATH_PROG(XMLTO, xmlto)
|
||||
if test x$XMLTO = x; then
|
||||
AC_MSG_ERROR([xmlto not found])
|
||||
fi
|
||||
|
||||
AC_SUBST(XMLTO)
|
||||
AM_CONDITIONAL(HAVE_XMLTO, test x$XMLTO != x)
|
||||
|
||||
AC_PATH_PROG(DOXYGEN, doxygen)
|
||||
if test x$DOXYGEN = x; then
|
||||
@@ -1445,8 +1711,6 @@ if test x$enable_documentation = xyes; then
|
||||
fi
|
||||
|
||||
AC_SUBST(DOXYGEN)
|
||||
else
|
||||
AM_CONDITIONAL(HAVE_XMLTO, false)
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_DOCUMENTATION, test x$enable_documentation = xyes)
|
||||
@@ -1481,8 +1745,12 @@ AC_LANG_PUSH([C++])
|
||||
AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
|
||||
AX_APPEND_COMPILE_FLAGS([-fno-threadsafe-statics])
|
||||
AX_APPEND_COMPILE_FLAGS([-fmerge-all-constants])
|
||||
|
||||
if test x$no_exceptions = xyes; then
|
||||
AX_APPEND_COMPILE_FLAGS([-fno-exceptions])
|
||||
AX_APPEND_COMPILE_FLAGS([-fno-rtti])
|
||||
fi
|
||||
|
||||
AX_APPEND_COMPILE_FLAGS([-ffast-math])
|
||||
AX_APPEND_COMPILE_FLAGS([-ftree-vectorize])
|
||||
AC_LANG_POP
|
||||
@@ -1549,9 +1817,14 @@ results(ipv6, "IPv6")
|
||||
results(tcp, "TCP")
|
||||
results(un,[UNIX Domain Sockets])
|
||||
|
||||
printf '\nStorage support:\n\t'
|
||||
results(nfs, [NFS])
|
||||
results(smbclient, [SMB])
|
||||
|
||||
printf '\nFile format support:\n\t'
|
||||
results(aac, [AAC])
|
||||
results(adplug, [AdPlug])
|
||||
results(dsd, [DSD])
|
||||
results(sidplay, [C64 SID])
|
||||
results(ffmpeg, [FFMPEG])
|
||||
results(flac, [FLAC])
|
||||
@@ -1574,6 +1847,7 @@ results(wildmidi, [WildMidi])
|
||||
|
||||
printf '\nOther features:\n\t'
|
||||
results(lsr, [libsamplerate])
|
||||
results(soxr, [libsoxr])
|
||||
results(libmpdclient, [libmpdclient])
|
||||
results(inotify, [inotify])
|
||||
results(sqlite, [SQLite])
|
||||
@@ -1607,6 +1881,7 @@ if
|
||||
printf '\nStreaming encoder support:\n\t'
|
||||
results(flac_encoder, [FLAC])
|
||||
results(lame_encoder, [LAME])
|
||||
results(shine_encoder, [Shine])
|
||||
results(vorbis_encoder, [Ogg Vorbis])
|
||||
results(opus, [Opus])
|
||||
results(twolame_encoder, [TwoLAME])
|
||||
@@ -1616,11 +1891,14 @@ fi
|
||||
printf '\nStreaming support:\n\t'
|
||||
results(cdio_paranoia, [CDIO_PARANOIA])
|
||||
results(curl,[CURL])
|
||||
results(despotify,[Despotify])
|
||||
results(smbclient,[SMBCLIENT])
|
||||
results(soundcloud,[Soundcloud])
|
||||
printf '\n\t'
|
||||
results(mms,[MMS])
|
||||
|
||||
printf '\nEvent loop:\n\t'
|
||||
printf $with_pollmethod
|
||||
|
||||
printf '\n\n##########################################\n\n'
|
||||
|
||||
echo 'Generating files needed for compilation'
|
||||
@@ -1630,7 +1908,7 @@ dnl Generate files
|
||||
dnl ---------------------------------------------------------------------------
|
||||
AC_CONFIG_FILES(Makefile)
|
||||
AC_CONFIG_FILES(doc/doxygen.conf)
|
||||
AC_CONFIG_FILES(mpd.service)
|
||||
AC_CONFIG_FILES(systemd/mpd.service)
|
||||
AC_OUTPUT
|
||||
|
||||
echo 'MPD is ready for compilation, type "make" to begin.'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"docbook/dtd/xml/4.2/docbookx.dtd">
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<book>
|
||||
<title>The Music Player Daemon - Developer's Manual</title>
|
||||
|
||||
@@ -10,7 +11,7 @@
|
||||
<para>
|
||||
This is a guide for those who wish to hack on the MPD source
|
||||
code. MPD is an open project, and we are always happy about
|
||||
contributions. So far, more than 50 people have contributed
|
||||
contributions. So far, more than 150 people have contributed
|
||||
patches.
|
||||
</para>
|
||||
|
||||
@@ -155,7 +156,53 @@ foo(const char *abc, int xyz)
|
||||
|
||||
<para>
|
||||
Send your patches to the mailing list:
|
||||
mpd-devel@musicpd.org
|
||||
<email>mpd-devel@musicpd.org</email> (<ulink
|
||||
url="http://mailman.blarg.de/listinfo/mpd-devel">subscribe
|
||||
here</ulink>)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<command>git pull</command> requests are preferred. Regular
|
||||
contributors can get <ulink
|
||||
url="http://git.musicpd.org/account-policy.html">an account on
|
||||
git.musicpd.org</ulink>, but any public git repository will do.
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<title>Development Tools</title>
|
||||
|
||||
<section>
|
||||
<title>Clang Static Analyzer</title>
|
||||
|
||||
<para>
|
||||
The <ulink url="http://clang-analyzer.llvm.org/">clang static
|
||||
analyzer</ulink> is a tool that helps find bugs. To run it on
|
||||
the MPD code base, install LLVM and clang. Configure MPD to
|
||||
use clang:
|
||||
</para>
|
||||
|
||||
<programlisting>./configure --enable-debug CXX=clang++ CC=clang ...</programlisting>
|
||||
|
||||
<para>
|
||||
It is recommended to use <option>--enable-debug</option>,
|
||||
because the analyzer takes advantage of
|
||||
<function>assert()</function> calls, which are only enabled in
|
||||
the debug build.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now run the analyzer:
|
||||
</para>
|
||||
|
||||
<programlisting>scan-build --use-c++=clang++ --use-cc=clang make</programlisting>
|
||||
|
||||
<para>
|
||||
The options <option>--use-c++</option> and
|
||||
<option>--use-cc</option> are necessary because it invokes
|
||||
<command>cc</command> for actually compiling the sources by
|
||||
default. That breaks, because MPD requires a C99 compiler.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
</book>
|
||||
|
||||
154
doc/include/tags.xml
Normal file
154
doc/include/tags.xml
Normal file
@@ -0,0 +1,154 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE itemizedlist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>artist</varname>: the artist name. Its meaning is not
|
||||
well-defined; see <varname>composer</varname> and
|
||||
<varname>performer</varname> for more specific tags.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>artistsort</varname>: same as
|
||||
<varname>artist</varname>, but for sorting. This usually omits
|
||||
prefixes such as "The".
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>album</varname>: the album name.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumsort</varname>: same as <varname>album</varname>,
|
||||
but for sorting.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumartist</varname>: on multi-artist albums, this is
|
||||
the artist name which shall be used for the whole album. The
|
||||
exact meaning of this tag is not well-defined.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumartistsort</varname>: same as
|
||||
<varname>albumartist</varname>, but for sorting.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>title</varname>: the song title.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>track</varname>: the track number within the album.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>name</varname>: a name for this song. This is not the
|
||||
song title. The exact meaning of this tag is not well-defined.
|
||||
It is often used by badly configured internet radio stations
|
||||
with broken tags to squeeze both the artist name and the song
|
||||
title in one tag.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>genre</varname>: the music genre.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>date</varname>: the song's release date. This is
|
||||
usually a 4-digit year.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>composer</varname>: the artist who composed the song.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>performer</varname>: the artist who performed the song.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>comment</varname>: a human-readable comment about this
|
||||
song. The exact meaning of this tag is not well-defined.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>disc</varname>: the disc number in a multi-disc album.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_artistid</varname>: the artist id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_albumid</varname>: the album id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_albumartistid</varname>: the album artist
|
||||
id in the <ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_trackid</varname>: the track id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_releasetrackid</varname>: the release track
|
||||
id in the <ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
101
doc/mpd.conf.5
101
doc/mpd.conf.5
@@ -136,53 +136,6 @@ for the format of this parameter. Multiple audio_output sections may be
|
||||
specified. If no audio_output section is specified, then MPD will scan for a
|
||||
usable audio output.
|
||||
.TP
|
||||
.B audio_output_format <sample_rate:bits:channels>
|
||||
This specifies the sample rate, bits per sample, and number of channels of
|
||||
audio that is sent to each audio output. Note that audio outputs may specify
|
||||
their own audio format which will be used for actual output to the audio
|
||||
device. An example is "44100:16:2" for 44100Hz, 16 bits, and 2 channels. The
|
||||
default is to use the audio format of the input file.
|
||||
Any of the three attributes may be an asterisk to specify that this
|
||||
attribute should not be enforced
|
||||
.TP
|
||||
.B samplerate_converter <integer or prefix>
|
||||
This specifies the libsamplerate converter to use. The supplied value should
|
||||
either be an integer or a prefix of the name of a converter. The default is
|
||||
"Fastest Sinc Interpolator".
|
||||
|
||||
At the time of this writing, the following converters are available:
|
||||
.RS
|
||||
.TP
|
||||
Best Sinc Interpolator (0)
|
||||
|
||||
Band limited sinc interpolation, best quality, 97dB SNR, 96% BW.
|
||||
.TP
|
||||
Medium Sinc Interpolator (1)
|
||||
|
||||
Band limited sinc interpolation, medium quality, 97dB SNR, 90% BW.
|
||||
.TP
|
||||
Fastest Sinc Interpolator (2)
|
||||
|
||||
Band limited sinc interpolation, fastest, 97dB SNR, 80% BW.
|
||||
.TP
|
||||
ZOH Interpolator (3)
|
||||
|
||||
Zero order hold interpolator, very fast, very poor quality with audible
|
||||
distortions.
|
||||
.TP
|
||||
Linear Interpolator (4)
|
||||
|
||||
Linear interpolator, very fast, poor quality.
|
||||
.TP
|
||||
internal
|
||||
|
||||
Poor quality, no floating point operations. This is the default (and
|
||||
only choice) if MPD was compiled without libsamplerate.
|
||||
.RE
|
||||
.IP
|
||||
For an up-to-date list of available converters, please see the libsamplerate
|
||||
documentation (available online at <\fBhttp://www.mega\-nerd.com/SRC/\fP>).
|
||||
.TP
|
||||
.B replaygain <off or album or track or auto>
|
||||
If specified, mpd will adjust the volume of songs played using ReplayGain tags
|
||||
(see <\fBhttp://www.replaygain.org/\fP>). Setting this to "album" will adjust
|
||||
@@ -198,39 +151,6 @@ This is the gain (in dB) applied to songs with ReplayGain tags.
|
||||
.B volume_normalization <yes or no>
|
||||
If yes, mpd will normalize the volume of songs as they play. The default is no.
|
||||
.TP
|
||||
.B audio_buffer_size <size in KiB>
|
||||
This specifies the size of the audio buffer in kibibytes. The default is 4096,
|
||||
large enough for nearly 12 seconds of CD-quality audio.
|
||||
.TP
|
||||
.B buffer_before_play <0-100%>
|
||||
This specifies how much of the audio buffer should be filled before playing a
|
||||
song. Try increasing this if you hear skipping when manually changing songs.
|
||||
The default is 10%, a little over 1 second of CD-quality audio with the default
|
||||
buffer size.
|
||||
.TP
|
||||
.B http_proxy_host <hostname>
|
||||
This setting is deprecated. Use the "proxy" setting in the "curl"
|
||||
input block. See MPD user manual for details.
|
||||
.TP
|
||||
.B connection_timeout <seconds>
|
||||
If a client does not send any new data in this time period, the connection is
|
||||
closed. The default is 60.
|
||||
.TP
|
||||
.B max_connections <number>
|
||||
This specifies the maximum number of clients that can be connected to mpd. The
|
||||
default is 5.
|
||||
.TP
|
||||
.B max_playlist_length <number>
|
||||
This specifies the maximum number of songs that can be in the playlist. The
|
||||
default is 16384.
|
||||
.TP
|
||||
.B max_command_list_size <size in KiB>
|
||||
This specifies the maximum size a command list can be. The default is 2048.
|
||||
.TP
|
||||
.B max_output_buffer_size <size in KiB>
|
||||
This specifies the maximum size of the output buffer to a client. The default
|
||||
is 8192.
|
||||
.TP
|
||||
.B filesystem_charset <charset>
|
||||
This specifies the character set used for the filesystem. A list of supported
|
||||
character sets can be obtained by running "iconv \-l". The default is
|
||||
@@ -254,17 +174,6 @@ MP3 playback.
|
||||
This specifies whether relative or absolute paths for song filenames are used
|
||||
when saving playlists. The default is "no".
|
||||
.TP
|
||||
.B metadata_to_use <tags>
|
||||
This specifies the tag types that will be scanned for and made available to
|
||||
clients. Note that you must recreate (not update) your database for changes to
|
||||
this parameter to take effect. Possible values are artist, album, title,
|
||||
track, name, genre, date, composer, performer, comment, disc,
|
||||
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
|
||||
musicbrainz_trackid. Multiple tags may be specified as a comma separated list.
|
||||
An example value is "artist,album,title,track". The special value "none" may
|
||||
be used alone to disable all metadata. The default is to use all known tag
|
||||
types except for comments and those starting with "musicbrainz".
|
||||
.TP
|
||||
.B auto_update <yes or no>
|
||||
This specifies the whether to support automatic update of music database when
|
||||
files are changed in music_directory. The default is to disable autoupdate
|
||||
@@ -274,16 +183,6 @@ of database.
|
||||
Limit the depth of the directories being watched, 0 means only watch
|
||||
the music directory itself. There is no limit by default.
|
||||
.TP
|
||||
.B despotify_user <name>
|
||||
This specifies the user to use when logging in to Spotify using the despotify plugins.
|
||||
.TP
|
||||
.B despotify_password <name>
|
||||
This specifies the password to use when logging in to Spotify using the despotify plugins.
|
||||
.TP
|
||||
.B despotify_high_bitrate <yes or no>
|
||||
This specifies if the requested bitrate for Spotify should be high or not. Higher sounds
|
||||
better but requires more processing and higher bandwidth. Default is yes.
|
||||
.TP
|
||||
.SH REQUIRED AUDIO OUTPUT PARAMETERS
|
||||
.TP
|
||||
.B type <type>
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. The complete list of possible values can be
|
||||
# found in the mpd.conf man page.
|
||||
# found in the user manual.
|
||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
@@ -373,38 +373,6 @@ input {
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# MPD Internal Buffering ######################################################
|
||||
#
|
||||
# This setting adjusts the size of internal decoded audio buffering. Changing
|
||||
# this may have undesired effects. Don't change this if you don't know what you
|
||||
# are doing.
|
||||
#
|
||||
#audio_buffer_size "4096"
|
||||
#
|
||||
# This setting controls the percentage of the buffer which is filled before
|
||||
# beginning to play. Increasing this reduces the chance of audio file skipping,
|
||||
# at the cost of increased time prior to audio playback.
|
||||
#
|
||||
#buffer_before_play "10%"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
|
||||
# Resource Limitations ########################################################
|
||||
#
|
||||
# These settings are various limitations to prevent MPD from using too many
|
||||
# resources. Generally, these settings should be minimized to prevent security
|
||||
# risks, depending on the operating resources.
|
||||
#
|
||||
#connection_timeout "60"
|
||||
#max_connections "10"
|
||||
#max_playlist_length "16384"
|
||||
#max_command_list_size "2048"
|
||||
#max_output_buffer_size "8192"
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# Character Encoding ##########################################################
|
||||
#
|
||||
# If file or directory names do not display correctly for your locale then you
|
||||
|
||||
497
doc/protocol.xml
497
doc/protocol.xml
@@ -1,21 +1,22 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"docbook/dtd/xml/4.2/docbookx.dtd">
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<book>
|
||||
<title>The Music Player Daemon protocol</title>
|
||||
|
||||
<chapter>
|
||||
<chapter id="syntax">
|
||||
<title>General protocol syntax</title>
|
||||
|
||||
<section>
|
||||
<title>Protocol overview</title>
|
||||
|
||||
<para>
|
||||
The MPD command protocol exchanges line-based text records
|
||||
between client and server over TCP. Once the client is
|
||||
connected to the server, they conduct a conversation until the
|
||||
client closes the connection. The conversation flow is always
|
||||
initiated by the client.
|
||||
The <application>MPD</application> command protocol exchanges
|
||||
line-based text records between client and server over TCP.
|
||||
Once the client is connected to the server, they conduct a
|
||||
conversation until the client closes the connection. The
|
||||
conversation flow is always initiated by the client.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -38,7 +39,7 @@
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="request_syntax">
|
||||
<title>Requests</title>
|
||||
|
||||
<cmdsynopsis>
|
||||
@@ -70,7 +71,7 @@
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="response_syntax">
|
||||
<title>Responses</title>
|
||||
|
||||
<para>
|
||||
@@ -79,7 +80,7 @@
|
||||
denote the end of command execution.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<section id="failure_response_syntax">
|
||||
<title>Failure responses</title>
|
||||
|
||||
<para>
|
||||
@@ -188,7 +189,7 @@
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="range_syntax">
|
||||
<title>Ranges</title>
|
||||
|
||||
<para>
|
||||
@@ -201,23 +202,42 @@
|
||||
omitted, then the maximum possible value is assumed.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="tags">
|
||||
<title>Tags</title>
|
||||
|
||||
<para>
|
||||
The following tags are supported by
|
||||
<application>MPD</application>:
|
||||
</para>
|
||||
|
||||
<xi:include href="include/tags.xml"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
|
||||
<para>
|
||||
There can be multiple values for some of these tags. For
|
||||
example, <application>MPD</application> may return multiple
|
||||
lines with a <varname>performer</varname> tag. A tag value is
|
||||
a UTF-8 string.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<chapter id="recipes">
|
||||
<title>Recipes</title>
|
||||
|
||||
<section>
|
||||
<section id="queuing_recipe">
|
||||
<title>Queuing</title>
|
||||
|
||||
<para>
|
||||
Often, users run MPD with "<link
|
||||
Often, users run <application>MPD</application> with "<link
|
||||
linkend="command_random">random</link>" enabled, but want to
|
||||
be able to insert songs "before" the rest of the playlist.
|
||||
That is commonly called "queuing".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
MPD implements this by allowing the client to specify a
|
||||
<application>MPD</application> implements this by allowing the client to specify a
|
||||
"priority" for each song in the playlist (commands <link
|
||||
linkend="command_prio"><command>prio</command></link> and
|
||||
<link
|
||||
@@ -227,24 +247,25 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In "random" mode, MPD maintains an internal randomized
|
||||
sequence of songs. In this sequence, songs with a higher
|
||||
priority come first, and all songs with the same priority are
|
||||
shuffled (by default, all songs are shuffled, because all have
|
||||
the same priority "0"). When you increase the priority of a
|
||||
song, it is moved to the front of the sequence according to
|
||||
its new priority, but always after the current one. A song
|
||||
that has been played already (it's "before" the current song
|
||||
in that sequence) will only be scheduled for repeated playback
|
||||
if its priority has become bigger than the priority of the
|
||||
current song. Decreasing the priority of a song will moved it
|
||||
farther to the end of the sequence. Changing the priority of
|
||||
the current song has no effect on the sequence.
|
||||
In "random" mode, <application>MPD</application> maintains an
|
||||
internal randomized sequence of songs. In this sequence,
|
||||
songs with a higher priority come first, and all songs with
|
||||
the same priority are shuffled (by default, all songs are
|
||||
shuffled, because all have the same priority "0"). When you
|
||||
increase the priority of a song, it is moved to the front of
|
||||
the sequence according to its new priority, but always after
|
||||
the current one. A song that has been played already (it's
|
||||
"before" the current song in that sequence) will only be
|
||||
scheduled for repeated playback if its priority has become
|
||||
bigger than the priority of the current song. Decreasing the
|
||||
priority of a song will moved it farther to the end of the
|
||||
sequence. Changing the priority of the current song has no
|
||||
effect on the sequence.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter>
|
||||
<chapter id="command_reference">
|
||||
<title>Command reference</title>
|
||||
|
||||
<note>
|
||||
@@ -255,12 +276,12 @@
|
||||
commands using song ids should be used instead of the commands
|
||||
that manipulate and control playback based on playlist
|
||||
position. Using song ids is a safer method when multiple
|
||||
clients are interacting with MPD.
|
||||
clients are interacting with <application>MPD</application>.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<section>
|
||||
<title>Querying MPD's status</title>
|
||||
<section id="status_commands">
|
||||
<title>Querying <application>MPD</application>'s status</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry id="command_clearerror">
|
||||
@@ -298,12 +319,14 @@
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<footnote id="since_0_14"><simpara>Introduced with MPD 0.14</simpara></footnote>
|
||||
<footnote id="since_0_14"><simpara>Introduced with
|
||||
<application>MPD</application> 0.14</simpara></footnote>
|
||||
Waits until there is a noteworthy change in one or more
|
||||
of MPD's subsystems. As soon as there is one, it lists
|
||||
all changed systems in a line in the format
|
||||
<returnvalue>changed: SUBSYSTEM</returnvalue>, where
|
||||
SUBSYSTEM is one of the following:
|
||||
of <application>MPD</application>'s subsystems. As soon
|
||||
as there is one, it lists all changed systems in a line
|
||||
in the format <returnvalue>changed:
|
||||
SUBSYSTEM</returnvalue>, where SUBSYSTEM is one of the
|
||||
following:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
@@ -385,14 +408,15 @@
|
||||
to wait for events as long as mpd runs. The
|
||||
<command>idle</command> command can be canceled by
|
||||
sending the command <command>noidle</command> (no other
|
||||
commands are allowed). MPD will then leave
|
||||
<command>idle</command> mode and print results
|
||||
immediately; might be empty at this time.
|
||||
commands are allowed). <application>MPD</application>
|
||||
will then leave <command>idle</command> mode and print
|
||||
results immediately; might be empty at this time.
|
||||
</para>
|
||||
<para>
|
||||
If the optional <varname>SUBSYSTEMS</varname> argument is used,
|
||||
MPD will only send notifications when something changed in
|
||||
one of the specified subsytems.
|
||||
If the optional <varname>SUBSYSTEMS</varname> argument
|
||||
is used, <application>MPD</application> will only send
|
||||
notifications when something changed in one of the
|
||||
specified subsytems.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -429,7 +453,7 @@
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>single</varname>:
|
||||
<footnote id="since_0_15"><simpara>Introduced with MPD 0.15</simpara></footnote>
|
||||
<footnote id="since_0_15"><simpara>Introduced with <application>MPD</application> 0.15</simpara></footnote>
|
||||
<returnvalue>0 or 1</returnvalue>
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -504,7 +528,7 @@
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>elapsed</varname>:
|
||||
<footnote id="since_0_16"><simpara>Introduced with MPD 0.16</simpara></footnote>
|
||||
<footnote id="since_0_16"><simpara>Introduced with <application>MPD</application> 0.16</simpara></footnote>
|
||||
<returnvalue>
|
||||
Total time elapsed within the current song, but
|
||||
with higher resolution.
|
||||
@@ -612,7 +636,7 @@
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="playback_option_commands">
|
||||
<title>Playback options</title>
|
||||
|
||||
<variablelist>
|
||||
@@ -745,7 +769,7 @@
|
||||
<parameter>album</parameter>,
|
||||
<parameter>auto</parameter><footnote
|
||||
id="replay_gain_auto_since_0_16">
|
||||
<simpara>added in MPD 0.16</simpara>
|
||||
<simpara>added in <application>MPD</application> 0.16</simpara>
|
||||
</footnote>.
|
||||
</para>
|
||||
<para>
|
||||
@@ -795,7 +819,7 @@
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="playback_commands">
|
||||
<title>Controlling playback</title>
|
||||
|
||||
<variablelist>
|
||||
@@ -882,8 +906,8 @@
|
||||
<listitem>
|
||||
<para>
|
||||
Seeks to the position <varname>TIME</varname> (in
|
||||
seconds) of entry <varname>SONGPOS</varname> in the
|
||||
playlist.
|
||||
seconds; fractions allowed) of entry
|
||||
<varname>SONGPOS</varname> in the playlist.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -898,7 +922,8 @@
|
||||
<listitem>
|
||||
<para>
|
||||
Seeks to the position <varname>TIME</varname> (in
|
||||
seconds) of song <varname>SONGID</varname>.
|
||||
seconds; fractions allowed) of song
|
||||
<varname>SONGID</varname>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -912,9 +937,10 @@
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Seeks to the position <varname>TIME</varname> within the
|
||||
current song. If prefixed by '+' or '-', then the time
|
||||
is relative to the current playing position.
|
||||
Seeks to the position <varname>TIME</varname> (in
|
||||
seconds; fractions allowed) within the current song. If
|
||||
prefixed by '+' or '-', then the time is relative to the
|
||||
current playing position.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -934,7 +960,7 @@
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="queue">
|
||||
<title>The current playlist</title>
|
||||
|
||||
<variablelist>
|
||||
@@ -1035,7 +1061,7 @@ OK
|
||||
at <varname>START:END</varname> to <varname>TO</varname>
|
||||
in the playlist.
|
||||
<footnote id="range_since_0_15">
|
||||
<simpara>Ranges are supported since MPD 0.15</simpara>
|
||||
<simpara>Ranges are supported since <application>MPD</application> 0.15</simpara>
|
||||
</footnote>
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -1135,7 +1161,7 @@ OK
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Searches case-sensitively for partial matches in the
|
||||
Searches case-insensitively for partial matches in the
|
||||
current playlist.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -1218,6 +1244,28 @@ OK
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_rangeid">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>rangeid</command>
|
||||
<arg choice="req"><replaceable>ID</replaceable></arg>
|
||||
<arg choice="req"><replaceable>START:END</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
<footnote id="since_0_19"><simpara>Since <application>MPD</application>
|
||||
0.19</simpara></footnote> Specifies the portion of the
|
||||
song that shall be played. <varname>START</varname> and
|
||||
<varname>END</varname> are offsets in seconds
|
||||
(fractional seconds allowed); both are optional.
|
||||
Omitting both (i.e. sending just ":") means "remove the
|
||||
range, play everything". A song that is currently
|
||||
playing cannot be manipulated this way.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_shuffle">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1263,10 +1311,48 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_addtagid">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>addtagid</command>
|
||||
<arg choice="req"><replaceable>SONGID</replaceable></arg>
|
||||
<arg choice="req"><replaceable>TAG</replaceable></arg>
|
||||
<arg choice="req"><replaceable>VALUE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Adds a tag to the specified song. Editing song tags is
|
||||
only possible for remote songs. This change is
|
||||
volatile: it may be overwritten by tags received from
|
||||
the server, and the data is gone when the song gets
|
||||
removed from the queue.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_cleartagid">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>cleartagid</command>
|
||||
<arg choice="req"><replaceable>SONGID</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>TAG</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Removes tags from the specified song. If
|
||||
<varname>TAG</varname> is not specified, then all tag
|
||||
values will be removed. Editing song tags is only
|
||||
possible for remote songs.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="playlist_files">
|
||||
<title>Stored playlists</title>
|
||||
|
||||
<para>
|
||||
@@ -1456,16 +1542,20 @@ OK
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="database">
|
||||
<title>The music database</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry id="command_count">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>count</command>
|
||||
<arg choice="req"><replaceable>TAG</replaceable></arg>
|
||||
<arg choice="req"><replaceable>NEEDLE</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
<arg choice="opt">group</arg>
|
||||
<arg choice="opt"><replaceable>GROUPTYPE</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
@@ -1473,8 +1563,15 @@ OK
|
||||
Counts the number of songs and their total playtime in
|
||||
the db matching <varname>TAG</varname> exactly.
|
||||
</para>
|
||||
<para>
|
||||
The <parameter>group</parameter> keyword may be used to
|
||||
group the results by a tag. The following prints
|
||||
per-artist counts:
|
||||
</para>
|
||||
<programlisting>count group artist</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_find">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1488,15 +1585,43 @@ OK
|
||||
<para>
|
||||
Finds songs in the db that are exactly
|
||||
<varname>WHAT</varname>. <varname>TYPE</varname> can
|
||||
be any tag supported by MPD, or one of the three special
|
||||
parameters — <parameter>file</parameter> to search by
|
||||
be any tag supported by <application>MPD</application>, or one of the special
|
||||
parameters:
|
||||
</para>
|
||||
|
||||
full path (relative to the music directory),
|
||||
<parameter>in</parameter> to restrict the search to
|
||||
songs in the given directory (also relative to the music
|
||||
directory) and
|
||||
<parameter>any</parameter> to match against all
|
||||
available tags. <varname>WHAT</varname> is what to find.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<parameter>any</parameter> checks all tag values
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<parameter>file</parameter> checks the full path
|
||||
(relative to the music directory)
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<parameter>base</parameter> restricts the search to
|
||||
songs in the given directory (also relative to the
|
||||
music directory)
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<parameter>modified-since</parameter> compares the
|
||||
file's time stamp with the given value (ISO 8601 or
|
||||
UNIX time stamp)
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<varname>WHAT</varname> is what to find.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -1517,27 +1642,43 @@ OK
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_list">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>list</command>
|
||||
<arg choice="req"><replaceable>TYPE</replaceable></arg>
|
||||
<arg><replaceable>ARTIST</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>FILTERTYPE</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>FILTERWHAT</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
<arg choice="opt">group</arg>
|
||||
<arg choice="opt"><replaceable>GROUPTYPE</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>...</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Lists all tags of the specified type.
|
||||
<varname>TYPE</varname> can be any tag supported by MPD or
|
||||
Lists unique tags values of the specified type.
|
||||
<varname>TYPE</varname> can be any tag supported by
|
||||
<application>MPD</application> or
|
||||
<parameter>file</parameter>.
|
||||
</para>
|
||||
<para>
|
||||
<varname>ARTIST</varname> is an optional parameter when
|
||||
type is album, this specifies to list albums by an
|
||||
artist.
|
||||
Additional arguments may specify a filter like the one
|
||||
in the <link
|
||||
linkend="command_find"><command>find</command>
|
||||
command</link>.
|
||||
</para>
|
||||
<para>
|
||||
The <parameter>group</parameter> keyword may be used
|
||||
(repeatedly) to group the results by one or more tags.
|
||||
The following example lists all album names,
|
||||
grouped by their respective (album) artist:
|
||||
</para>
|
||||
<programlisting>list album group albumartist</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_listall">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
@@ -1550,6 +1691,14 @@ OK
|
||||
Lists all songs and directories in
|
||||
<varname>URI</varname>.
|
||||
</para>
|
||||
<para>
|
||||
Do not use this command. Do not manage a client-side
|
||||
copy of <application>MPD</application>'s database. That
|
||||
is fragile and adds huge overhead. It will break with
|
||||
large databases. Instead, query
|
||||
<application>MPD</application> whenever you need
|
||||
something.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_listallinfo">
|
||||
@@ -1565,6 +1714,40 @@ OK
|
||||
returns metadata info in the same format as
|
||||
<command>lsinfo</command>.
|
||||
</para>
|
||||
<para>
|
||||
Do not use this command. Do not manage a client-side
|
||||
copy of <application>MPD</application>'s database. That
|
||||
is fragile and adds huge overhead. It will break with
|
||||
large databases. Instead, query
|
||||
<application>MPD</application> whenever you need
|
||||
something.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_listfiles">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>listfiles</command>
|
||||
<arg><replaceable>URI</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Lists the contents of the directory
|
||||
<varname>URI</varname>, including files are not
|
||||
recognized by <application>MPD</application>.
|
||||
<varname>URI</varname> can be a path relative to the
|
||||
music directory or an URI understood by one of the
|
||||
storage plugins. The response contains at least one
|
||||
line for each directory entry with the prefix "file: "
|
||||
or "directory: ", and may be followed by file attributes
|
||||
such as "Last-Modified" and "size".
|
||||
</para>
|
||||
<para>
|
||||
For example, "smb://SERVER" returns a list of all shares
|
||||
on the given SMB/CIFS server; "nfs://servername/path"
|
||||
obtains a directory listing from the NFS server.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry id="command_lsinfo">
|
||||
@@ -1584,6 +1767,10 @@ OK
|
||||
the list of stored playlists. This behavior is
|
||||
deprecated; use "listplaylists" instead.
|
||||
</para>
|
||||
<para>
|
||||
This command may be used to list metadata of remote
|
||||
files (e.g. URI beginning with "http://" or "smb://").
|
||||
</para>
|
||||
<para>
|
||||
Clients that are connected via UNIX domain socket may
|
||||
use this command to read the tags of an arbitrary local
|
||||
@@ -1605,6 +1792,10 @@ OK
|
||||
to the music directory or a URL in the form
|
||||
"file:///foo/bar.ogg".
|
||||
</para>
|
||||
<para>
|
||||
This command may be used to list metadata of remote
|
||||
files (e.g. URI beginning with "http://" or "smb://").
|
||||
</para>
|
||||
<para>
|
||||
The response consists of lines in the form "KEY: VALUE".
|
||||
Comments with suspicious characters (e.g. newlines) are
|
||||
@@ -1722,22 +1913,131 @@ OK
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="mount">
|
||||
<title>Mounts and neighbors</title>
|
||||
|
||||
<para>
|
||||
A "storage" provides access to files in a directory tree. The
|
||||
most basic storage plugin is the "local" storage plugin which
|
||||
accesses the local file system, and there are plugins to
|
||||
access NFS and SMB servers.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Multiple storages can be "mounted" together, similar to the
|
||||
<application>mount</application> command on many operating
|
||||
systems, but without cooperation from the kernel. No
|
||||
superuser privileges are necessary, beause this mapping exists
|
||||
only inside the <application>MPD</application> process
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry id="command_mount">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>mount</command>
|
||||
<arg choice="req"><replaceable>PATH</replaceable></arg>
|
||||
<arg choice="req"><replaceable>URI</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Mount the specified remote storage URI at the given
|
||||
path. Example:
|
||||
</para>
|
||||
|
||||
<programlisting>mount foo nfs://192.168.1.4/export/mp3</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_umount">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>unmount</command>
|
||||
<arg choice="req"><replaceable>PATH</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Unmounts the specified path. Example:
|
||||
</para>
|
||||
|
||||
<programlisting>unmount foo</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_listmounts">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>listmounts</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Queries a list of all mounts. By default, this contains
|
||||
just the configured <varname>music_directory</varname>.
|
||||
Example:
|
||||
</para>
|
||||
|
||||
<programlisting>listmounts
|
||||
mount:
|
||||
storage: /home/foo/music
|
||||
mount: foo
|
||||
storage: nfs://192.168.1.4/export/mp3
|
||||
OK
|
||||
</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="command_listneighbors">
|
||||
<term>
|
||||
<cmdsynopsis>
|
||||
<command>listneighbors</command>
|
||||
</cmdsynopsis>
|
||||
</term>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Queries a list of "neighbors" (e.g. accessible file
|
||||
servers on the local net). Items on that list may be
|
||||
used with the <link
|
||||
linkend="command_mount"><command>mount</command></link>
|
||||
command. Example:
|
||||
</para>
|
||||
|
||||
<programlisting>listneighbors
|
||||
neighbor: smb://FOO
|
||||
name: FOO (Samba 4.1.11-Debian)
|
||||
OK
|
||||
</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section id="stickers">
|
||||
<title>Stickers</title>
|
||||
|
||||
<para>
|
||||
"Stickers"<footnoteref linkend="since_0_15"/> are pieces of
|
||||
information attached to existing MPD objects (e.g. song files,
|
||||
information attached to existing
|
||||
<application>MPD</application> objects (e.g. song files,
|
||||
directories, albums). Clients can create arbitrary name/value
|
||||
pairs. MPD itself does not assume any special meaning in
|
||||
them.
|
||||
pairs. <application>MPD</application> itself does not assume
|
||||
any special meaning in them.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The goal is to allow clients to share additional (possibly
|
||||
dynamic) information about songs, which is neither stored on
|
||||
the client (not available to other clients), nor stored in the
|
||||
song files (MPD has no write access).
|
||||
song files (<application>MPD</application> has no write
|
||||
access).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -1842,7 +2142,7 @@ OK
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="connection_commands">
|
||||
<title>Connection settings</title>
|
||||
|
||||
<variablelist>
|
||||
@@ -1854,7 +2154,8 @@ OK
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Closes the connection to MPD. MPD will try to send the
|
||||
Closes the connection to <application>MPD</application>.
|
||||
<application>MPD</application> will try to send the
|
||||
remaining output buffer before it actually closes the
|
||||
connection, but that cannot be guaranteed. This command
|
||||
will not generate a response.
|
||||
@@ -1869,7 +2170,7 @@ OK
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Kills MPD.
|
||||
Kills <application>MPD</application>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -1903,7 +2204,7 @@ OK
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="output_commands">
|
||||
<title>Audio output devices</title>
|
||||
|
||||
<variablelist>
|
||||
@@ -1957,12 +2258,38 @@ OK
|
||||
<para>
|
||||
Shows information about all outputs.
|
||||
</para>
|
||||
<screen>
|
||||
outputid: 0
|
||||
outputname: My ALSA Device
|
||||
outputenabled: 0
|
||||
OK
|
||||
</screen>
|
||||
<para>
|
||||
Return information:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>outputid</varname>: ID of the output. May change between executions
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>outputname</varname>: Name of the output. It can be any.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>outputenabled</varname>: Status of the output. 0 if disabled, 1 if enabled.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="reflection_commands">
|
||||
<title>Reflection</title>
|
||||
|
||||
<variablelist>
|
||||
@@ -2078,7 +2405,7 @@ suffix: mpc</programlisting>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="client_to_client">
|
||||
<title>Client to client</title>
|
||||
|
||||
<para>
|
||||
|
||||
1807
doc/user.xml
1807
doc/user.xml
File diff suppressed because it is too large
Load Diff
@@ -54,10 +54,12 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
#serial 4
|
||||
|
||||
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
|
||||
[for flag in $1; do
|
||||
[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
for flag in $1; do
|
||||
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3])
|
||||
done
|
||||
])dnl AX_APPEND_COMPILE_FLAGS
|
||||
|
||||
@@ -52,10 +52,12 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
#serial 4
|
||||
|
||||
AC_DEFUN([AX_APPEND_LINK_FLAGS],
|
||||
[for flag in $1; do
|
||||
[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
for flag in $1; do
|
||||
AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3])
|
||||
done
|
||||
])dnl AX_APPEND_LINK_FLAGS
|
||||
|
||||
272
m4/ax_boost_base.m4
Normal file
272
m4/ax_boost_base.m4
Normal file
@@ -0,0 +1,272 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for the Boost C++ libraries of a particular version (or newer)
|
||||
#
|
||||
# If no path to the installed boost library is given the macro searchs
|
||||
# under /usr, /usr/local, /opt and /opt/local and evaluates the
|
||||
# $BOOST_ROOT environment variable. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2009 Peter Adolphs
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 23
|
||||
|
||||
AC_DEFUN([AX_BOOST_BASE],
|
||||
[
|
||||
AC_ARG_WITH([boost],
|
||||
[AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
|
||||
[use Boost library from a standard location (ARG=yes),
|
||||
from the specified location (ARG=<path>),
|
||||
or disable it (ARG=no)
|
||||
@<:@ARG=yes@:>@ ])],
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ac_boost_path=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ac_boost_path="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"])
|
||||
|
||||
|
||||
AC_ARG_WITH([boost-libdir],
|
||||
AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
|
||||
[Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
|
||||
[
|
||||
if test -d "$withval"
|
||||
then
|
||||
ac_boost_lib_path="$withval"
|
||||
else
|
||||
AC_MSG_ERROR(--with-boost-libdir expected directory name)
|
||||
fi
|
||||
],
|
||||
[ac_boost_lib_path=""]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
|
||||
boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
|
||||
boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
|
||||
boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
|
||||
boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
|
||||
if test "x$boost_lib_version_req_sub_minor" = "x" ; then
|
||||
boost_lib_version_req_sub_minor="0"
|
||||
fi
|
||||
WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
|
||||
AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
|
||||
succeeded=no
|
||||
|
||||
dnl On 64-bit systems check for system libraries in both lib64 and lib.
|
||||
dnl The former is specified by FHS, but e.g. Debian does not adhere to
|
||||
dnl this (as it rises problems for generic multi-arch support).
|
||||
dnl The last entry in the list is chosen by default when no libraries
|
||||
dnl are found, e.g. when only header-only libraries are installed!
|
||||
libsubdirs="lib"
|
||||
ax_arch=`uname -m`
|
||||
case $ax_arch in
|
||||
x86_64|ppc64|s390x|sparc64|aarch64)
|
||||
libsubdirs="lib64 lib lib64"
|
||||
;;
|
||||
esac
|
||||
|
||||
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
|
||||
dnl them priority over the other paths since, if libs are found there, they
|
||||
dnl are almost assuredly the ones desired.
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
|
||||
|
||||
case ${host_cpu} in
|
||||
i?86)
|
||||
libsubdirs="lib/i386-${host_os} $libsubdirs"
|
||||
;;
|
||||
esac
|
||||
|
||||
dnl first we check the system location for boost libraries
|
||||
dnl this location ist chosen if boost libraries are installed with the --layout=system option
|
||||
dnl or if you install boost with RPM
|
||||
if test "$ac_boost_path" != ""; then
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path/include"
|
||||
for ac_boost_path_tmp in $libsubdirs; do
|
||||
if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
|
||||
BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif test "$cross_compiling" != yes; then
|
||||
for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
|
||||
if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
dnl overwrite ld flags if we have required special directory with
|
||||
dnl --with-boost-libdir parameter
|
||||
if test "$ac_boost_lib_path" != ""; then
|
||||
BOOST_LDFLAGS="-L$ac_boost_lib_path"
|
||||
fi
|
||||
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_REQUIRE([AC_PROG_CXX])
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
@%:@include <boost/version.hpp>
|
||||
]], [[
|
||||
#if BOOST_VERSION >= $WANT_BOOST_VERSION
|
||||
// Everything is okay
|
||||
#else
|
||||
# error Boost version is too old
|
||||
#endif
|
||||
]])],[
|
||||
AC_MSG_RESULT(yes)
|
||||
succeeded=yes
|
||||
found_system=yes
|
||||
],[
|
||||
])
|
||||
AC_LANG_POP([C++])
|
||||
|
||||
|
||||
|
||||
dnl if we found no boost with system layout we search for boost libraries
|
||||
dnl built and installed without the --layout=system option or for a staged(not installed) version
|
||||
if test "x$succeeded" != "xyes"; then
|
||||
_version=0
|
||||
if test "$ac_boost_path" != ""; then
|
||||
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
|
||||
for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
|
||||
_version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
|
||||
V_CHECK=`expr $_version_tmp \> $_version`
|
||||
if test "$V_CHECK" = "1" ; then
|
||||
_version=$_version_tmp
|
||||
fi
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
|
||||
done
|
||||
fi
|
||||
else
|
||||
if test "$cross_compiling" != yes; then
|
||||
for ac_boost_path in /usr /usr/local /opt /opt/local ; do
|
||||
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
|
||||
for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
|
||||
_version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
|
||||
V_CHECK=`expr $_version_tmp \> $_version`
|
||||
if test "$V_CHECK" = "1" ; then
|
||||
_version=$_version_tmp
|
||||
best_path=$ac_boost_path
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
|
||||
if test "$ac_boost_lib_path" = ""; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$best_path/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "x$BOOST_ROOT" != "x"; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
|
||||
version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
|
||||
stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
|
||||
stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
|
||||
V_CHECK=`expr $stage_version_shorten \>\= $_version`
|
||||
if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
|
||||
AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
|
||||
BOOST_CPPFLAGS="-I$BOOST_ROOT"
|
||||
BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
@%:@include <boost/version.hpp>
|
||||
]], [[
|
||||
#if BOOST_VERSION >= $WANT_BOOST_VERSION
|
||||
// Everything is okay
|
||||
#else
|
||||
# error Boost version is too old
|
||||
#endif
|
||||
]])],[
|
||||
AC_MSG_RESULT(yes)
|
||||
succeeded=yes
|
||||
found_system=yes
|
||||
],[
|
||||
])
|
||||
AC_LANG_POP([C++])
|
||||
fi
|
||||
|
||||
if test "$succeeded" != "yes" ; then
|
||||
if test "$_version" = "0" ; then
|
||||
AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
|
||||
else
|
||||
AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
|
||||
fi
|
||||
# execute ACTION-IF-NOT-FOUND (if present):
|
||||
ifelse([$3], , :, [$3])
|
||||
else
|
||||
AC_SUBST(BOOST_CPPFLAGS)
|
||||
AC_SUBST(BOOST_LDFLAGS)
|
||||
AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
|
||||
# execute ACTION-IF-FOUND (if present):
|
||||
ifelse([$2], , :, [$2])
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
|
||||
])
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
@@ -19,6 +19,8 @@
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
@@ -53,7 +55,7 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
#serial 3
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
|
||||
@@ -61,7 +63,7 @@ AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
||||
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
|
||||
# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
@@ -19,6 +19,8 @@
|
||||
# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
|
||||
# issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_LINK_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
|
||||
#
|
||||
@@ -53,14 +55,14 @@
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 2
|
||||
#serial 3
|
||||
|
||||
AC_DEFUN([AX_CHECK_LINK_FLAG],
|
||||
[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS $4 $1"
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM()],
|
||||
AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
LDFLAGS=$ax_check_save_flags])
|
||||
|
||||
332
m4/ax_pthread.m4
Normal file
332
m4/ax_pthread.m4
Normal file
@@ -0,0 +1,332 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads. It
|
||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
|
||||
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
|
||||
# PTHREAD_CFLAGS.
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||
# will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or if
|
||||
# you have any other suggestions or comments. This macro was based on work
|
||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||
# grateful for the helpful feedback of numerous users.
|
||||
#
|
||||
# Updated for Autoconf 2.68 by Daniel Richard G.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program 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 General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 21
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||
AC_DEFUN([AX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_PUSH([C])
|
||||
ax_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
|
||||
AC_MSG_RESULT([$ax_pthread_ok])
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# ... -mt is also the pthreads flag for HP/aCC
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case ${host_os} in
|
||||
solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
|
||||
;;
|
||||
|
||||
darwin*)
|
||||
ax_pthread_flags="-pthread $ax_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clang doesn't consider unrecognized options an error unless we specify
|
||||
# -Werror. We throw in some extra Clang-specific options to ensure that
|
||||
# this doesn't happen for GCC, which also accepts -Werror.
|
||||
|
||||
AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
|
||||
save_CFLAGS="$CFLAGS"
|
||||
ax_pthread_extra_flags="-Werror"
|
||||
CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[ax_pthread_extra_flags=
|
||||
AC_MSG_RESULT([no])])
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
for flag in $ax_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
|
||||
if test x"$ax_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
|
||||
static void routine(void *a) { a = 0; }
|
||||
static void *start_routine(void *a) { return a; }],
|
||||
[pthread_t th; pthread_attr_t attr;
|
||||
pthread_create(&th, 0, start_routine, 0);
|
||||
pthread_join(th, 0);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_cleanup_push(routine, 0);
|
||||
pthread_cleanup_pop(0) /* ; */])],
|
||||
[ax_pthread_ok=yes],
|
||||
[])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT([$ax_pthread_ok])
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
attr_name=unknown
|
||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
|
||||
[int attr = $attr; return attr /* ; */])],
|
||||
[attr_name=$attr; break],
|
||||
[])
|
||||
done
|
||||
AC_MSG_RESULT([$attr_name])
|
||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
|
||||
[Define to necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case ${host_os} in
|
||||
aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
|
||||
osf* | hpux*) flag="-D_REENTRANT";;
|
||||
solaris*)
|
||||
if test "$GCC" = "yes"; then
|
||||
flag="-D_REENTRANT"
|
||||
else
|
||||
# TODO: What about Clang on Solaris?
|
||||
flag="-mt -D_REENTRANT"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT([$flag])
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT], [
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
|
||||
[[int i = PTHREAD_PRIO_INHERIT;]])],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=no])
|
||||
])
|
||||
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
|
||||
[AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# More AIX lossage: compile with *_r variant
|
||||
if test "x$GCC" != xyes; then
|
||||
case $host_os in
|
||||
aix*)
|
||||
AS_CASE(["x/$CC"],
|
||||
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
|
||||
[#handle absolute path differently from PATH based program lookup
|
||||
AS_CASE(["x$CC"],
|
||||
[x/*],
|
||||
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
|
||||
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
||||
|
||||
AC_SUBST([PTHREAD_LIBS])
|
||||
AC_SUBST([PTHREAD_CFLAGS])
|
||||
AC_SUBST([PTHREAD_CC])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$ax_pthread_ok" = xyes; then
|
||||
ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
|
||||
:
|
||||
else
|
||||
ax_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_POP
|
||||
])dnl AX_PTHREAD
|
||||
37
m4/ax_require_defined.m4
Normal file
37
m4/ax_require_defined.m4
Normal file
@@ -0,0 +1,37 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_require_defined.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_REQUIRE_DEFINED(MACRO)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
|
||||
# been defined and thus are available for use. This avoids random issues
|
||||
# where a macro isn't expanded. Instead the configure script emits a
|
||||
# non-fatal:
|
||||
#
|
||||
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
|
||||
#
|
||||
# It's like AC_REQUIRE except it doesn't expand the required macro.
|
||||
#
|
||||
# Here's an example:
|
||||
#
|
||||
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 1
|
||||
|
||||
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
|
||||
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
|
||||
])dnl AX_REQUIRE_DEFINED
|
||||
31
m4/faad.m4
31
m4/faad.m4
@@ -62,36 +62,7 @@ int main() {
|
||||
CPPFLAGS=$oldcppflags
|
||||
fi
|
||||
|
||||
if test x$enable_aac = xyes; then
|
||||
oldcflags=$CFLAGS
|
||||
oldlibs=$LIBS
|
||||
oldcppflags=$CPPFLAGS
|
||||
CFLAGS="$CFLAGS $FAAD_CFLAGS -Werror"
|
||||
LIBS="$LIBS $FAAD_LIBS"
|
||||
CPPFLAGS=$CFLAGS
|
||||
|
||||
AC_MSG_CHECKING(for broken libfaad headers)
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||
#include <faad.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int main() {
|
||||
unsigned char channels;
|
||||
uint32_t sample_rate;
|
||||
|
||||
NeAACDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
||||
return 0;
|
||||
}
|
||||
])],
|
||||
[AC_MSG_RESULT(correct)],
|
||||
[AC_MSG_RESULT(broken);
|
||||
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
||||
|
||||
CFLAGS=$oldcflags
|
||||
LIBS=$oldlibs
|
||||
CPPFLAGS=$oldcppflags
|
||||
else
|
||||
if test x$enable_aac = xno; then
|
||||
FAAD_LIBS=""
|
||||
FAAD_CFLAGS=""
|
||||
fi
|
||||
|
||||
9
m4/mpd_depends.m4
Normal file
9
m4/mpd_depends.m4
Normal file
@@ -0,0 +1,9 @@
|
||||
AC_DEFUN([MPD_DEPENDS], [
|
||||
if test x$$2 = xno; then
|
||||
if test x$$1 = xauto; then
|
||||
$1=no
|
||||
elif test x$$1 = xyes; then
|
||||
AC_MSG_ERROR([$3])
|
||||
fi
|
||||
fi
|
||||
])
|
||||
@@ -10,3 +10,16 @@ AC_DEFUN([MPD_OPTIONAL_FUNC], [
|
||||
[AC_CHECK_FUNC([$2],
|
||||
[AC_DEFINE([$3], 1, [Define to use $1])],)])
|
||||
])
|
||||
|
||||
dnl MPD_OPTIONAL_FUNC_NODEF(name, func)
|
||||
dnl
|
||||
dnl Allow the user to enable or disable the use of a function.
|
||||
dnl Works similar to MPD_OPTIONAL_FUNC, however MPD_OPTIONAL_FUNC_NODEF
|
||||
dnl does not invoke AC_DEFINE when function is enabled. Shell variable
|
||||
dnl enable_$name is set to "yes" instead.
|
||||
AC_DEFUN([MPD_OPTIONAL_FUNC_NODEF], [
|
||||
AC_ARG_ENABLE([$1],
|
||||
AS_HELP_STRING([--enable-$1],
|
||||
[use the function "$1" (default: auto)]),,
|
||||
[AC_CHECK_FUNC([$2], [enable_$1=yes],)])
|
||||
])
|
||||
|
||||
107
m4/pkg.m4
107
m4/pkg.m4
@@ -1,4 +1,5 @@
|
||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
# serial 1 (pkg-config-0.24)
|
||||
#
|
||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
#
|
||||
@@ -25,8 +26,12 @@
|
||||
# ----------------------------------
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
|
||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
|
||||
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
|
||||
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
|
||||
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
@@ -39,7 +44,6 @@ if test -n "$PKG_CONFIG"; then
|
||||
AC_MSG_RESULT([no])
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
|
||||
fi[]dnl
|
||||
])# PKG_PROG_PKG_CONFIG
|
||||
|
||||
@@ -48,32 +52,30 @@ fi[]dnl
|
||||
# Check to see whether a particular set of modules exists. Similar
|
||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
#
|
||||
#
|
||||
# Similar to PKG_CHECK_MODULES, make sure that the first instance of
|
||||
# this or PKG_CHECK_MODULES is called, or make sure to call
|
||||
# PKG_CHECK_EXISTS manually
|
||||
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
# only at the first occurence in configure.ac, so if the first place
|
||||
# it's called might be skipped (such as if it is within an "if", you
|
||||
# have to call PKG_CHECK_EXISTS manually
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||
m4_ifval([$2], [$2], [:])
|
||||
m4_default([$2], [:])
|
||||
m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
|
||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
# ---------------------------------------------
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$PKG_CONFIG"; then
|
||||
if test -n "$$1"; then
|
||||
[if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
else
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
PKG_CHECK_EXISTS([$3],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes ],
|
||||
[pkg_failed=yes])
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
@@ -117,16 +119,17 @@ and $1[]_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.])
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
AC_MSG_RESULT([no])
|
||||
_PKG_SHORT_ERRORS_SUPPORTED
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
|
||||
else
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||
|
||||
ifelse([$4], , [AC_MSG_ERROR(dnl
|
||||
m4_default([$4], [AC_MSG_ERROR(
|
||||
[Package requirements ($2) were not met:
|
||||
|
||||
$$1_PKG_ERRORS
|
||||
@@ -134,24 +137,78 @@ $$1_PKG_ERRORS
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
_PKG_TEXT
|
||||
])],
|
||||
[AC_MSG_RESULT([no])
|
||||
$4])
|
||||
_PKG_TEXT])[]dnl
|
||||
])
|
||||
elif test $pkg_failed = untried; then
|
||||
ifelse([$4], , [AC_MSG_FAILURE(dnl
|
||||
AC_MSG_RESULT([no])
|
||||
m4_default([$4], [AC_MSG_FAILURE(
|
||||
[The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
_PKG_TEXT
|
||||
|
||||
To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])],
|
||||
[$4])
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
|
||||
])
|
||||
else
|
||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||
AC_MSG_RESULT([yes])
|
||||
ifelse([$3], , :, [$3])
|
||||
$3
|
||||
fi[]dnl
|
||||
])# PKG_CHECK_MODULES
|
||||
|
||||
|
||||
# PKG_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable pkgconfigdir as the location where a module
|
||||
# should install pkg-config .pc files. By default the directory is
|
||||
# $libdir/pkgconfig, but the default can be changed by passing
|
||||
# DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
# parameter.
|
||||
AC_DEFUN([PKG_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
|
||||
[with_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
]) dnl PKG_INSTALLDIR
|
||||
|
||||
|
||||
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
# module should install arch-independent pkg-config .pc files. By
|
||||
# default the directory is $datadir/pkgconfig, but the default can be
|
||||
# changed by passing DIRECTORY. The user can override through the
|
||||
# --with-noarch-pkgconfigdir parameter.
|
||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
|
||||
AC_ARG_WITH([noarch-pkgconfigdir],
|
||||
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
|
||||
[with_noarch_pkgconfigdir=]pkg_default)
|
||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
]) dnl PKG_NOARCH_INSTALLDIR
|
||||
|
||||
|
||||
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# -------------------------------------------
|
||||
# Retrieves the value of the pkg-config variable for the given module.
|
||||
AC_DEFUN([PKG_CHECK_VAR],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||
|
||||
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])# PKG_CHECK_VAR
|
||||
|
||||
16
m4/ucred.m4
16
m4/ucred.m4
@@ -1,5 +1,4 @@
|
||||
# Check if "struct ucred" is available. If not, try harder with
|
||||
# _GNU_SOURCE.
|
||||
# Check if "struct ucred" is available.
|
||||
#
|
||||
# Author: Max Kellermann <max@duempel.org>
|
||||
|
||||
@@ -10,19 +9,6 @@ AC_DEFUN([STRUCT_UCRED],[
|
||||
[struct ucred cred;],
|
||||
mpd_cv_have_struct_ucred=yes,
|
||||
mpd_cv_have_struct_ucred=no)
|
||||
if test x$mpd_cv_have_struct_ucred = xno; then
|
||||
# glibc 2.8 forces _GNU_SOURCE on us
|
||||
AC_TRY_COMPILE(
|
||||
[#define _GNU_SOURCE
|
||||
#include <sys/socket.h>],
|
||||
[struct ucred cred;],
|
||||
mpd_cv_have_struct_ucred=yes,
|
||||
mpd_cv_have_struct_ucred=no)
|
||||
if test x$mpd_cv_have_struct_ucred = xyes; then
|
||||
# :(
|
||||
CFLAGS="$CFLAGS -D_GNU_SOURCE"
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
AC_MSG_RESULT($mpd_cv_have_struct_ucred)
|
||||
|
||||
857
mpd.svg
Normal file
857
mpd.svg
Normal file
@@ -0,0 +1,857 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Sodipodi ("http://www.sodipodi.com/") -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
x="0"
|
||||
y="0"
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg1"
|
||||
sodipodi:version="0.32"
|
||||
sodipodi:docname="mpd.svg"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-test5.png"
|
||||
inkscape:export-xdpi="76.799988"
|
||||
inkscape:export-ydpi="76.799988">
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
inkscape:zoom="2.6884788"
|
||||
inkscape:cx="71.610485"
|
||||
inkscape:cy="61.484977"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="994"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
showgrid="false"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 80 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="160 : 80 : 1"
|
||||
inkscape:persp3d-origin="80 : 53.333333 : 1"
|
||||
id="perspective118" />
|
||||
<linearGradient
|
||||
id="linearGradient919">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.86092716;"
|
||||
offset="0.0000000"
|
||||
id="stop920" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop921" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1068">
|
||||
<stop
|
||||
offset="0.0000000"
|
||||
style="stop-color:#d2d2d2;stop-opacity:1.0000000;"
|
||||
id="stop1070" />
|
||||
<stop
|
||||
offset="1.0000000"
|
||||
style="stop-color:#ffffff;stop-opacity:1.0000000;"
|
||||
id="stop1069" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1065">
|
||||
<stop
|
||||
offset="0.0000000"
|
||||
style="stop-color:#ffffff;stop-opacity:1.0000000;"
|
||||
id="stop1067" />
|
||||
<stop
|
||||
offset="1.0000000"
|
||||
style="stop-color:#c2bfbf;stop-opacity:0.99607843;"
|
||||
id="stop1066" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1060">
|
||||
<stop
|
||||
offset="0.0000000"
|
||||
style="stop-color:#878787;stop-opacity:1.0000000;"
|
||||
id="stop1063" />
|
||||
<stop
|
||||
offset="1.0000000"
|
||||
style="stop-color:#000000;stop-opacity:0.99607843;"
|
||||
id="stop1061" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient645">
|
||||
<stop
|
||||
style="stop-color:#aca597;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop646" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop647" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient593">
|
||||
<stop
|
||||
style="stop-color:#478acf;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop594" />
|
||||
<stop
|
||||
style="stop-color:#65c6f7;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop595" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient574">
|
||||
<stop
|
||||
style="stop-color:#85ad92;stop-opacity:1.0000;"
|
||||
offset="0"
|
||||
id="stop575" />
|
||||
<stop
|
||||
style="stop-color:#559db2;stop-opacity:0.7725;"
|
||||
offset="1"
|
||||
id="stop576" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient570">
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:0.7176;"
|
||||
offset="0"
|
||||
id="stop571" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.3725;"
|
||||
offset="1"
|
||||
id="stop572" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient573"
|
||||
xlink:href="#linearGradient1068"
|
||||
x1="40.458553"
|
||||
y1="389.65582"
|
||||
x2="36.063946"
|
||||
y2="357.28375"
|
||||
gradientTransform="matrix(2.3025192,0,0,0.29683004,-0.91913426,-1.5117091)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient1213"
|
||||
xlink:href="#linearGradient1068"
|
||||
x1="123.71407"
|
||||
y1="141.41566"
|
||||
x2="98.353867"
|
||||
y2="113.41083"
|
||||
gradientTransform="matrix(0.91680324,0,0,0.74547827,-0.91913426,-1.5117091)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
id="radialGradient581"
|
||||
xlink:href="#linearGradient919"
|
||||
cx="0.095785439"
|
||||
cy="0.16814159"
|
||||
r="1.5409589"
|
||||
fx="0.095785439"
|
||||
fy="0.16814159" />
|
||||
<linearGradient
|
||||
id="linearGradient580"
|
||||
xlink:href="#linearGradient1068"
|
||||
x1="132.0352"
|
||||
y1="135.68469"
|
||||
x2="119.62381"
|
||||
y2="111.07157"
|
||||
gradientTransform="matrix(0.90170536,0,0,0.75796032,-0.91913426,-1.5117091)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1060"
|
||||
id="linearGradient901"
|
||||
x1="0.93491787"
|
||||
y1="0.92044502"
|
||||
x2="-0.052546836"
|
||||
y2="0.20347559" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient593"
|
||||
id="linearGradient902" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1068"
|
||||
id="linearGradient916"
|
||||
x1="0.14831461"
|
||||
y1="-1.6875"
|
||||
x2="0.43370786"
|
||||
y2="1.8125" />
|
||||
<defs
|
||||
id="defs890">
|
||||
<linearGradient
|
||||
id="linearGradient922"
|
||||
x1="0"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad"
|
||||
xlink:href="#linearGradient1065" />
|
||||
<linearGradient
|
||||
id="linearGradient908"
|
||||
x1="0"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad"
|
||||
xlink:href="#linearGradient1060" />
|
||||
<linearGradient
|
||||
id="linearGradient894"
|
||||
x1="0"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad"
|
||||
xlink:href="#linearGradient1068" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient894"
|
||||
id="linearGradient897"
|
||||
x1="0.5955056"
|
||||
y1="-0.33587787"
|
||||
x2="0.61348313"
|
||||
y2="1.1908396" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient894"
|
||||
id="linearGradient898"
|
||||
x1="0.96449792"
|
||||
y1="1.0278323"
|
||||
x2="0.46738392"
|
||||
y2="0.21800731" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient908"
|
||||
id="linearGradient907"
|
||||
x1="0.57078654"
|
||||
y1="2.3770492"
|
||||
x2="0.33258426"
|
||||
y2="0.49180329" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient922"
|
||||
id="linearGradient921"
|
||||
x1="0.47058824"
|
||||
y1="0.15384616"
|
||||
x2="0.46547315"
|
||||
y2="0.98380566" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient922"
|
||||
id="linearGradient948" />
|
||||
<defs
|
||||
id="defs987">
|
||||
<linearGradient
|
||||
id="linearGradient855"
|
||||
x1="0"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad"
|
||||
xlink:href="#linearGradient908" />
|
||||
<linearGradient
|
||||
id="linearGradient1188"
|
||||
x1="0"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad"
|
||||
xlink:href="#linearGradient922" />
|
||||
<linearGradient
|
||||
id="linearGradient831">
|
||||
<stop
|
||||
style="stop-color:#94897f;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop832" />
|
||||
<stop
|
||||
style="stop-color:#fff5fe;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop833" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1188"
|
||||
id="linearGradient834"
|
||||
x1="0.87550199"
|
||||
y1="0.34817815"
|
||||
x2="-0.29317269"
|
||||
y2="0.93522269"
|
||||
gradientUnits="objectBoundingBox"
|
||||
spreadMethod="pad" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient1188"
|
||||
id="radialGradient835"
|
||||
r="0.55628061"
|
||||
fy="0.28125"
|
||||
fx="0.59090906"
|
||||
cy="0.28125"
|
||||
cx="0.59090906"
|
||||
spreadMethod="pad" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1188"
|
||||
id="linearGradient893"
|
||||
x1="0.12793733"
|
||||
y1="0.76923078"
|
||||
x2="0.49608356"
|
||||
y2="0.70850199" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient855"
|
||||
id="linearGradient625"
|
||||
x1="0.035955057"
|
||||
y1="1.0276498"
|
||||
x2="0.053932585"
|
||||
y2="-0.359447" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1188"
|
||||
id="linearGradient627"
|
||||
x1="1.2826855"
|
||||
y1="0.12550607"
|
||||
x2="-0.15547703"
|
||||
y2="0.96356273" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient1188"
|
||||
id="radialGradient628"
|
||||
r="1.5982224"
|
||||
fy="0.4866707"
|
||||
fx="0.36789617"
|
||||
cy="0.4866707"
|
||||
cx="0.36789617"
|
||||
gradientTransform="scale(0.877379,1.139758)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1188"
|
||||
id="linearGradient628"
|
||||
x1="0.76923078"
|
||||
y1="0.14979757"
|
||||
x2="0.41909814"
|
||||
y2="0.73279351" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview898"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.4732669"
|
||||
inkscape:cx="50.051177"
|
||||
inkscape:cy="18.096983"
|
||||
inkscape:window-width="1022"
|
||||
inkscape:window-height="670"
|
||||
showguides="false"
|
||||
snaptoguides="false"
|
||||
showgrid="false"
|
||||
snaptogrid="false"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25">
|
||||
<sodipodi:guide
|
||||
orientation="vertical"
|
||||
position="28.705556"
|
||||
id="guide879" />
|
||||
<sodipodi:guide
|
||||
orientation="horizontal"
|
||||
position="30.130655"
|
||||
id="guide880" />
|
||||
</sodipodi:namedview>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview1003"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.3368738"
|
||||
inkscape:cx="24.541029"
|
||||
inkscape:cy="14.368596"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="499"
|
||||
showguides="true"
|
||||
snaptoguides="true"
|
||||
inkscape:window-x="138"
|
||||
inkscape:window-y="169" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1060"
|
||||
id="linearGradient1304"
|
||||
x1="-0.20218579"
|
||||
y1="0.21681416"
|
||||
x2="0.67759562"
|
||||
y2="0.57522124" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient1065"
|
||||
id="linearGradient1322"
|
||||
x1="0.32404181"
|
||||
y1="0.77876109"
|
||||
x2="0.24041812"
|
||||
y2="0.26548672" />
|
||||
<defs
|
||||
id="defs989">
|
||||
<linearGradient
|
||||
id="linearGradient850">
|
||||
<stop
|
||||
style="stop-color:#eed680;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop852" />
|
||||
<stop
|
||||
style="stop-color:#dfb546;stop-opacity:1.0000000;"
|
||||
offset="0.68035328"
|
||||
id="stop858" />
|
||||
<stop
|
||||
style="stop-color:#d8a429;stop-opacity:1.0000000;"
|
||||
offset="0.77277374"
|
||||
id="stop859" />
|
||||
<stop
|
||||
style="stop-color:#d1940c;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop857" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient850"
|
||||
id="linearGradient569"
|
||||
x1="0.11875"
|
||||
y1="0.12612613"
|
||||
x2="0.59375"
|
||||
y2="0.66066068"
|
||||
spreadMethod="pad" />
|
||||
<linearGradient
|
||||
id="linearGradient839">
|
||||
<stop
|
||||
style="stop-color:#46a046;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop840" />
|
||||
<stop
|
||||
style="stop-color:#df421e;stop-opacity:1.0000000;"
|
||||
offset="0.39364964"
|
||||
id="stop841" />
|
||||
<stop
|
||||
style="stop-color:#ada7c8;stop-opacity:1.0000000;"
|
||||
offset="0.72036445"
|
||||
id="stop842" />
|
||||
<stop
|
||||
style="stop-color:#eed680;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop843" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient839"
|
||||
id="linearGradient836"
|
||||
x1="1.3267924e-17"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5" />
|
||||
<defs
|
||||
id="defs604">
|
||||
<linearGradient
|
||||
id="linearGradient622">
|
||||
<stop
|
||||
style="stop-color:#f8e29d;stop-opacity:0.4471;"
|
||||
offset="0"
|
||||
id="stop623" />
|
||||
<stop
|
||||
style="stop-color:#272d2d;stop-opacity:0.4784;"
|
||||
offset="1"
|
||||
id="stop624" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient617">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop618" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop619" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient613">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.6235;"
|
||||
offset="0"
|
||||
id="stop614" />
|
||||
<stop
|
||||
style="stop-color:#5d6567;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop615" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient607">
|
||||
<stop
|
||||
style="stop-color:#d7d5d5;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop608" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.4039;"
|
||||
offset="1"
|
||||
id="stop609" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient607"
|
||||
id="radialGradient610"
|
||||
cx="1.4287461"
|
||||
cy="0.75323397"
|
||||
r="0.85534656"
|
||||
fx="1.4287461"
|
||||
fy="0.75323397"
|
||||
gradientTransform="matrix(1,2.268336e-6,-1.975559e-5,1,5.713033e-8,3.856326e-8)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient617"
|
||||
id="linearGradient612"
|
||||
x1="7.7024956"
|
||||
y1="-2.0263922"
|
||||
x2="62.759903"
|
||||
y2="56.137772"
|
||||
gradientTransform="scale(0.9953779,1.0046436)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient613"
|
||||
id="radialGradient616"
|
||||
cx="58.70882"
|
||||
cy="53.831562"
|
||||
r="43.551846"
|
||||
fx="58.70882"
|
||||
fy="53.831562"
|
||||
gradientTransform="scale(0.99517298,1.0048504)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient617"
|
||||
id="linearGradient621" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient617"
|
||||
id="linearGradient626"
|
||||
x1="72.060211"
|
||||
y1="55.161442"
|
||||
x2="32.409"
|
||||
y2="12.126946"
|
||||
gradientTransform="matrix(0.995134,-1.068631e-5,-1.31398e-7,1.00489,0,0)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient607"
|
||||
id="linearGradient687"
|
||||
x1="67.707405"
|
||||
y1="49.314793"
|
||||
x2="-10.031048"
|
||||
y2="4.6068792"
|
||||
gradientTransform="scale(0.99522839,1.0047945)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient617"
|
||||
id="linearGradient742"
|
||||
x1="-7.4378386"
|
||||
y1="25.923714"
|
||||
x2="18.009745"
|
||||
y2="10.089797"
|
||||
gradientTransform="scale(0.889853,1.123781)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview889"
|
||||
showguides="true"
|
||||
snaptoguides="true"
|
||||
inkscape:zoom="7.5625000"
|
||||
inkscape:cx="24.000000"
|
||||
inkscape:cy="24.000000"
|
||||
inkscape:window-width="640"
|
||||
inkscape:window-height="496"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="26" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview1023"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.5521067"
|
||||
inkscape:cx="66.459318"
|
||||
inkscape:cy="62.629296"
|
||||
inkscape:window-width="1150"
|
||||
inkscape:window-height="752"
|
||||
showgrid="true"
|
||||
snaptogrid="true"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="29">
|
||||
<inkscape:grid
|
||||
id="GridFromPre046Settings"
|
||||
type="xygrid"
|
||||
originx="0px"
|
||||
originy="0px"
|
||||
spacingx="1.0000000mm"
|
||||
spacingy="1.0000000mm"
|
||||
color="#0000ff"
|
||||
empcolor="#0000ff"
|
||||
opacity="0.2"
|
||||
empopacity="0.4"
|
||||
empspacing="5" />
|
||||
</sodipodi:namedview>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1068"
|
||||
id="linearGradient2924"
|
||||
x1="41.673889"
|
||||
y1="320.40921"
|
||||
x2="36.082947"
|
||||
y2="279.22458"
|
||||
gradientTransform="matrix(2.3376099,0,0,0.29237422,-0.91913426,-1.5117091)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1068"
|
||||
id="linearGradient2926"
|
||||
x1="134.95444"
|
||||
y1="108.16693"
|
||||
x2="102.05431"
|
||||
y2="71.835884"
|
||||
gradientTransform="matrix(0.91680324,0,0,0.74547824,-0.91913426,-1.5117091)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1068"
|
||||
id="linearGradient2928"
|
||||
x1="145.32188"
|
||||
y1="101.97199"
|
||||
x2="129.22044"
|
||||
y2="70.041069"
|
||||
gradientTransform="matrix(0.90170536,0,0,0.75796032,-0.91913426,-1.5117091)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<rect
|
||||
style="fill-opacity:0.47154475;fill-rule:evenodd;stroke-width:3pt"
|
||||
id="rect918"
|
||||
width="48.72493"
|
||||
height="42.16835"
|
||||
ry="0.74231374"
|
||||
x="67.536102"
|
||||
y="66.474693"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.7811048" />
|
||||
<rect
|
||||
style="fill-opacity:0.47154475;fill-rule:evenodd;stroke-width:3pt"
|
||||
id="rect1006"
|
||||
width="63.211483"
|
||||
height="54.705563"
|
||||
ry="0.74231374"
|
||||
x="64.47226"
|
||||
y="30.558294"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.7811048" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.47058824;fill-rule:evenodd"
|
||||
id="rect1005"
|
||||
width="57.843418"
|
||||
height="9.0050545"
|
||||
ry="0.62889248"
|
||||
x="65.398254"
|
||||
y="82.153206"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.66175652" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2924);fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.82671446"
|
||||
id="rect1007"
|
||||
width="54.910637"
|
||||
height="6.1445785"
|
||||
ry="0.42912331"
|
||||
x="64.622299"
|
||||
y="82.282539"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.44939452" />
|
||||
<rect
|
||||
width="57.905403"
|
||||
height="47.084496"
|
||||
ry="1.7822117"
|
||||
x="63.784973"
|
||||
y="32.456562"
|
||||
style="font-size:12px;fill:url(#linearGradient2926);fill-rule:evenodd;stroke:#000000;stroke-width:0"
|
||||
id="rect1009"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="1.6336281" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.47058824;fill-rule:evenodd"
|
||||
id="rect971"
|
||||
width="44.58709"
|
||||
height="6.9413123"
|
||||
ry="0.62889248"
|
||||
x="68.249886"
|
||||
y="106.24529"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.66175652" />
|
||||
<rect
|
||||
width="64.637024"
|
||||
height="54.068516"
|
||||
ry="1.4120796"
|
||||
x="59.853096"
|
||||
y="28.740753"
|
||||
style="font-size:12px;fill:url(#linearGradient2928);fill-rule:evenodd;stroke:#000000;stroke-width:1.65869105"
|
||||
id="rect1008"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="1.3970968" />
|
||||
<rect
|
||||
width="51.129478"
|
||||
height="39.964478"
|
||||
ry="0.5422883"
|
||||
x="66.358932"
|
||||
y="34.89621"
|
||||
style="font-size:12px;fill:#00b4ed;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.79054338;stroke-linejoin:round"
|
||||
id="rect976"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.5422883" />
|
||||
<metadata
|
||||
id="metadata982">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<rect
|
||||
width="44.634872"
|
||||
height="36.293858"
|
||||
ry="1.7822117"
|
||||
x="67.006332"
|
||||
y="67.937927"
|
||||
style="font-size:12px;fill:url(#linearGradient1213);fill-rule:evenodd;stroke:#000000;stroke-width:0"
|
||||
id="rect575"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="1.633628" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="font-size:12px;fill:#444040;fill-opacity:0.47058824;fill-rule:evenodd"
|
||||
id="path672"
|
||||
d="m 68.473,57.85183 a 23.629898,3.2222576 0 1 1 -47.259797,0 23.629898,3.2222576 0 1 1 47.259797,0 z"
|
||||
sodipodi:cx="44.843102"
|
||||
sodipodi:cy="57.85183"
|
||||
sodipodi:rx="23.629898"
|
||||
sodipodi:ry="3.2222576"
|
||||
transform="matrix(1.4221482,0,-0.30247168,1.9834766,9.6201687,-10.428817)"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big2.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="font-size:12px;fill:#4e4d4b;fill-rule:evenodd;stroke:#000000;stroke-width:2.30019999;stroke-opacity:0.9565"
|
||||
id="path625"
|
||||
d="m 58.291138,27.531645 a 19.367088,19.556963 0 1 1 -38.734177,0 19.367088,19.556963 0 1 1 38.734177,0 z"
|
||||
sodipodi:cx="38.924049"
|
||||
sodipodi:cy="27.531645"
|
||||
sodipodi:rx="19.367088"
|
||||
sodipodi:ry="19.556963"
|
||||
transform="matrix(-1.0172416,-0.47376693,-0.5523759,1.3286212,116.84611,57.272851)"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big2.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="font-size:12px;fill:url(#linearGradient612);fill-rule:evenodd;stroke:#000000;stroke-width:2.06100011;stroke-opacity:0.9565"
|
||||
id="path605"
|
||||
d="m 58.291138,27.531645 a 19.367088,19.556963 0 1 1 -38.734177,0 19.367088,19.556963 0 1 1 38.734177,0 z"
|
||||
sodipodi:cx="38.924049"
|
||||
sodipodi:cy="27.531645"
|
||||
sodipodi:rx="19.367088"
|
||||
sodipodi:ry="19.556963"
|
||||
transform="matrix(-1.4321234,-0.79696518,-1.1299666,2.2349846,128.07685,29.383033)"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big2.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="font-size:12px;fill:url(#radialGradient616);fill-rule:evenodd;stroke:#000000;stroke-width:0.317;stroke-opacity:0.97829997"
|
||||
id="path606"
|
||||
d="m 58.291138,27.531645 a 19.367088,19.556963 0 1 1 -38.734177,0 19.367088,19.556963 0 1 1 38.734177,0 z"
|
||||
sodipodi:cx="38.924049"
|
||||
sodipodi:cy="27.531645"
|
||||
sodipodi:rx="19.367088"
|
||||
sodipodi:ry="19.556963"
|
||||
transform="matrix(-1.1546358,-0.69851175,-0.95634664,1.9588777,108.06887,31.115628)"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big2.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="font-size:12px;fill-rule:evenodd;stroke-width:1.63499999"
|
||||
id="path686"
|
||||
d="m 58.291138,27.531645 a 19.367088,19.556963 0 1 1 -38.734177,0 19.367088,19.556963 0 1 1 38.734177,0 z"
|
||||
sodipodi:cx="38.924049"
|
||||
sodipodi:cy="27.531645"
|
||||
sodipodi:rx="19.367088"
|
||||
sodipodi:ry="19.556963"
|
||||
transform="matrix(-0.39495459,-0.4546194,-0.52881207,0.94219495,73.198184,52.427791)"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big2.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="font-size:12px;fill:url(#linearGradient687);fill-rule:evenodd;stroke:#3f3b3b;stroke-width:0.77380002"
|
||||
id="path611"
|
||||
d="m 58.291138,27.531645 a 19.367088,19.556963 0 1 1 -38.734177,0 19.367088,19.556963 0 1 1 38.734177,0 z"
|
||||
sodipodi:cx="38.924049"
|
||||
sodipodi:cy="27.531645"
|
||||
sodipodi:rx="19.367088"
|
||||
sodipodi:ry="19.556963"
|
||||
transform="matrix(-0.36949013,-0.40957751,-0.49471918,0.84885391,70.248021,52.066881)"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big2.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient573);fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:0.82671446"
|
||||
id="rect934"
|
||||
width="42.326431"
|
||||
height="4.7363882"
|
||||
ry="0.42912331"
|
||||
x="67.651756"
|
||||
y="106.34497"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.44939449" />
|
||||
<rect
|
||||
width="49.823761"
|
||||
height="41.677303"
|
||||
ry="1.4120796"
|
||||
x="63.975548"
|
||||
y="65.073692"
|
||||
style="font-size:12px;fill:url(#linearGradient580);fill-rule:evenodd;stroke:#000000;stroke-width:1.27855873;stroke-opacity:1"
|
||||
id="rect562"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="1.3970969" />
|
||||
<rect
|
||||
width="39.411831"
|
||||
height="30.805574"
|
||||
ry="0.5422883"
|
||||
x="68.990387"
|
||||
y="70.097008"
|
||||
style="font-size:12px;fill:#003d88;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.61407685;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.93023257"
|
||||
id="rect975"
|
||||
inkscape:export-filename="/cowserver/documents/httpd/vhosts/images/mpd-big7.png"
|
||||
inkscape:export-xdpi="721.66998"
|
||||
inkscape:export-ydpi="721.66998"
|
||||
rx="0.5422883" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 27 KiB |
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,9 +21,9 @@
|
||||
#include "AudioConfig.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "AudioParser.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "config/ConfigData.hxx"
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "config/ConfigOption.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
226
src/Chrono.hxx
Normal file
226
src/Chrono.hxx
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_CHRONO_HXX
|
||||
#define MPD_CHRONO_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(__GNUC__) && !GCC_CHECK_VERSION(4,7) && !defined(__clang__)
|
||||
/* std::chrono::duration operators are "constexpr" since gcc 4.7 */
|
||||
#define chrono_constexpr gcc_pure
|
||||
#else
|
||||
#define chrono_constexpr constexpr
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A time stamp within a song. Granularity is 1 millisecond and the
|
||||
* maximum value is about 49 days.
|
||||
*/
|
||||
class SongTime : public std::chrono::duration<std::uint32_t, std::milli> {
|
||||
typedef std::chrono::duration<std::uint32_t, std::milli> Base;
|
||||
typedef Base::rep rep;
|
||||
|
||||
public:
|
||||
SongTime() = default;
|
||||
|
||||
template<typename T>
|
||||
explicit constexpr SongTime(T t):Base(t) {}
|
||||
|
||||
static constexpr SongTime zero() {
|
||||
return SongTime(Base::zero());
|
||||
}
|
||||
|
||||
static constexpr SongTime FromS(unsigned s) {
|
||||
return SongTime(rep(s) * 1000);
|
||||
}
|
||||
|
||||
static constexpr SongTime FromS(float s) {
|
||||
return SongTime(rep(s * 1000));
|
||||
}
|
||||
|
||||
static constexpr SongTime FromS(double s) {
|
||||
return SongTime(rep(s * 1000));
|
||||
}
|
||||
|
||||
static constexpr SongTime FromMS(rep ms) {
|
||||
return SongTime(ms);
|
||||
}
|
||||
|
||||
constexpr rep ToS() const {
|
||||
return count() / rep(1000);
|
||||
}
|
||||
|
||||
constexpr rep RoundS() const {
|
||||
return (count() + 500) / rep(1000);
|
||||
}
|
||||
|
||||
constexpr rep ToMS() const {
|
||||
return count();
|
||||
}
|
||||
|
||||
template<typename T=rep>
|
||||
constexpr T ToScale(unsigned scale) const {
|
||||
return count() * T(scale) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a scalar value with the given scale to a #SongTime
|
||||
* instance.
|
||||
*
|
||||
* @param value the input value
|
||||
* @param scale the value's scale in Hz
|
||||
*/
|
||||
template<typename T=rep>
|
||||
static constexpr SongTime FromScale(T value, unsigned scale) {
|
||||
return SongTime(value * T(1000) / T(scale));
|
||||
}
|
||||
|
||||
constexpr double ToDoubleS() const {
|
||||
return double(count()) / 1000.;
|
||||
};
|
||||
|
||||
constexpr bool IsZero() const {
|
||||
return count() == 0;
|
||||
}
|
||||
|
||||
constexpr bool IsPositive() const {
|
||||
return count() > 0;
|
||||
}
|
||||
|
||||
chrono_constexpr SongTime operator+(const SongTime &other) const {
|
||||
return SongTime(*(const Base *)this + (const Base &)other);
|
||||
}
|
||||
|
||||
chrono_constexpr SongTime operator-(const SongTime &other) const {
|
||||
return SongTime(*(const Base *)this - (const Base &)other);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A variant of #SongTime that is based on a signed integer. It can
|
||||
* be used for relative values.
|
||||
*/
|
||||
class SignedSongTime : public std::chrono::duration<std::int32_t, std::milli> {
|
||||
typedef std::chrono::duration<std::int32_t, std::milli> Base;
|
||||
typedef Base::rep rep;
|
||||
|
||||
public:
|
||||
SignedSongTime() = default;
|
||||
|
||||
template<typename T>
|
||||
explicit constexpr SignedSongTime(T t):Base(t) {}
|
||||
|
||||
/**
|
||||
* Allow implicit conversion from SongTime to SignedSongTime.
|
||||
*/
|
||||
constexpr SignedSongTime(SongTime t):Base(t) {}
|
||||
|
||||
static constexpr SignedSongTime zero() {
|
||||
return SignedSongTime(Base::zero());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a negative value.
|
||||
*/
|
||||
static constexpr SignedSongTime Negative() {
|
||||
return SignedSongTime(-1);
|
||||
}
|
||||
|
||||
static constexpr SignedSongTime FromS(int s) {
|
||||
return SignedSongTime(rep(s) * 1000);
|
||||
}
|
||||
|
||||
static constexpr SignedSongTime FromS(unsigned s) {
|
||||
return SignedSongTime(rep(s) * 1000);
|
||||
}
|
||||
|
||||
static constexpr SignedSongTime FromS(float s) {
|
||||
return SignedSongTime(rep(s * 1000));
|
||||
}
|
||||
|
||||
static constexpr SignedSongTime FromS(double s) {
|
||||
return SignedSongTime(rep(s * 1000));
|
||||
}
|
||||
|
||||
static constexpr SignedSongTime FromMS(rep ms) {
|
||||
return SignedSongTime(ms);
|
||||
}
|
||||
|
||||
constexpr rep ToS() const {
|
||||
return count() / rep(1000);
|
||||
}
|
||||
|
||||
constexpr rep RoundS() const {
|
||||
return (count() + 500) / rep(1000);
|
||||
}
|
||||
|
||||
constexpr rep ToMS() const {
|
||||
return count();
|
||||
}
|
||||
|
||||
template<typename T=rep>
|
||||
constexpr T ToScale(unsigned scale) const {
|
||||
return count() * T(scale) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a scalar value with the given scale to a
|
||||
* #SignedSongTime instance.
|
||||
*
|
||||
* @param value the input value
|
||||
* @param scale the value's scale in Hz
|
||||
*/
|
||||
template<typename T=rep>
|
||||
static constexpr SignedSongTime FromScale(T value, unsigned scale) {
|
||||
return SignedSongTime(value * T(1000) / T(scale));
|
||||
}
|
||||
|
||||
constexpr double ToDoubleS() const {
|
||||
return double(count()) / 1000.;
|
||||
};
|
||||
|
||||
constexpr bool IsZero() const {
|
||||
return count() == 0;
|
||||
}
|
||||
|
||||
constexpr bool IsPositive() const {
|
||||
return count() > 0;
|
||||
}
|
||||
|
||||
constexpr bool IsNegative() const {
|
||||
return count() < 0;
|
||||
}
|
||||
|
||||
chrono_constexpr SignedSongTime operator+(const SignedSongTime &other) const {
|
||||
return SignedSongTime(*(const Base *)this + (const Base &)other);
|
||||
}
|
||||
|
||||
chrono_constexpr SignedSongTime operator-(const SignedSongTime &other) const {
|
||||
return SignedSongTime(*(const Base *)this - (const Base &)other);
|
||||
}
|
||||
};
|
||||
|
||||
#undef chrono_constexpr
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,57 +22,115 @@
|
||||
#include "ls.hxx"
|
||||
#include "LogInit.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "DecoderList.hxx"
|
||||
#include "DecoderPlugin.hxx"
|
||||
#include "OutputList.hxx"
|
||||
#include "OutputPlugin.hxx"
|
||||
#include "InputRegistry.hxx"
|
||||
#include "InputPlugin.hxx"
|
||||
#include "PlaylistRegistry.hxx"
|
||||
#include "PlaylistPlugin.hxx"
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "decoder/DecoderList.hxx"
|
||||
#include "decoder/DecoderPlugin.hxx"
|
||||
#include "output/Registry.hxx"
|
||||
#include "output/OutputPlugin.hxx"
|
||||
#include "input/Registry.hxx"
|
||||
#include "input/InputPlugin.hxx"
|
||||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "playlist/PlaylistPlugin.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "util/OptionDef.hxx"
|
||||
#include "util/OptionParser.hxx"
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/Registry.hxx"
|
||||
#include "db/DatabasePlugin.hxx"
|
||||
#include "storage/Registry.hxx"
|
||||
#include "storage/StoragePlugin.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
#include "neighbor/Registry.hxx"
|
||||
#include "neighbor/NeighborPlugin.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ENCODER
|
||||
#include "EncoderList.hxx"
|
||||
#include "EncoderPlugin.hxx"
|
||||
#include "encoder/EncoderList.hxx"
|
||||
#include "encoder/EncoderPlugin.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
#include "ArchiveList.hxx"
|
||||
#include "ArchivePlugin.hxx"
|
||||
#include "archive/ArchiveList.hxx"
|
||||
#include "archive/ArchivePlugin.hxx"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define CONFIG_FILE_LOCATION "\\mpd\\mpd.conf"
|
||||
#define CONFIG_FILE_LOCATION "mpd\\mpd.conf"
|
||||
#define APP_CONFIG_FILE_LOCATION "conf\\mpd.conf"
|
||||
#else
|
||||
#define USER_CONFIG_FILE_LOCATION1 ".mpdconf"
|
||||
#define USER_CONFIG_FILE_LOCATION2 ".mpd/mpd.conf"
|
||||
#define USER_CONFIG_FILE_LOCATION_XDG "mpd/mpd.conf"
|
||||
#endif
|
||||
|
||||
static constexpr OptionDef opt_kill(
|
||||
"kill", "kill the currently running mpd session");
|
||||
static constexpr OptionDef opt_no_config(
|
||||
"no-config", "don't read from config");
|
||||
static constexpr OptionDef opt_no_daemon(
|
||||
"no-daemon", "don't detach from console");
|
||||
static constexpr OptionDef opt_stdout(
|
||||
"stdout", nullptr); // hidden, compatibility with old versions
|
||||
static constexpr OptionDef opt_stderr(
|
||||
"stderr", "print messages to stderr");
|
||||
static constexpr OptionDef opt_verbose(
|
||||
"verbose", 'v', "verbose logging");
|
||||
static constexpr OptionDef opt_version(
|
||||
"version", 'V', "print version number");
|
||||
static constexpr OptionDef opt_help(
|
||||
"help", 'h', "show help options");
|
||||
static constexpr OptionDef opt_help_alt(
|
||||
nullptr, '?', nullptr); // hidden, standard alias for --help
|
||||
|
||||
static constexpr Domain cmdline_domain("cmdline");
|
||||
|
||||
gcc_noreturn
|
||||
static void version(void)
|
||||
{
|
||||
puts("Music Player Daemon " VERSION "\n"
|
||||
puts("Music Player Daemon " VERSION
|
||||
#ifdef GIT_COMMIT
|
||||
" (" GIT_COMMIT ")"
|
||||
#endif
|
||||
"\n"
|
||||
"\n"
|
||||
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
|
||||
"Copyright (C) 2008-2013 Max Kellermann <max@duempel.org>\n"
|
||||
"Copyright (C) 2008-2014 Max Kellermann <max@duempel.org>\n"
|
||||
"This is free software; see the source for copying conditions. There is NO\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
|
||||
"\n"
|
||||
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
puts("\n"
|
||||
"Database plugins:");
|
||||
|
||||
for (auto i = database_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
|
||||
puts("\n\n"
|
||||
"Storage plugins:");
|
||||
|
||||
for (auto i = storage_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
puts("\n\n"
|
||||
"Neighbor plugins:");
|
||||
for (auto i = neighbor_plugins; *i != nullptr; ++i)
|
||||
printf(" %s", (*i)->name);
|
||||
#endif
|
||||
|
||||
puts("\n\n"
|
||||
"Decoders plugins:");
|
||||
|
||||
decoder_plugins_for_each([](const DecoderPlugin &plugin){
|
||||
@@ -132,122 +190,165 @@ static void version(void)
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static const char *summary =
|
||||
"Music Player Daemon - a daemon for playing music.";
|
||||
|
||||
gcc_pure
|
||||
static AllocatedPath
|
||||
PathBuildChecked(const AllocatedPath &a, PathTraits::const_pointer b)
|
||||
static void PrintOption(const OptionDef &opt)
|
||||
{
|
||||
if (a.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
if (opt.HasShortOption())
|
||||
printf(" -%c, --%-12s%s\n",
|
||||
opt.GetShortOption(),
|
||||
opt.GetLongOption(),
|
||||
opt.GetDescription());
|
||||
else
|
||||
printf(" --%-16s%s\n",
|
||||
opt.GetLongOption(),
|
||||
opt.GetDescription());
|
||||
}
|
||||
|
||||
return AllocatedPath::Build(a, b);
|
||||
gcc_noreturn
|
||||
static void help(void)
|
||||
{
|
||||
puts("Usage:\n"
|
||||
" mpd [OPTION...] [path/to/mpd.conf]\n"
|
||||
"\n"
|
||||
"Music Player Daemon - a daemon for playing music.\n"
|
||||
"\n"
|
||||
"Options:");
|
||||
|
||||
PrintOption(opt_help);
|
||||
PrintOption(opt_kill);
|
||||
PrintOption(opt_no_config);
|
||||
PrintOption(opt_no_daemon);
|
||||
PrintOption(opt_stderr);
|
||||
PrintOption(opt_verbose);
|
||||
PrintOption(opt_version);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
class ConfigLoader
|
||||
{
|
||||
Error &error;
|
||||
bool result;
|
||||
public:
|
||||
ConfigLoader(Error &_error) : error(_error), result(false) { }
|
||||
|
||||
bool GetResult() const { return result; }
|
||||
|
||||
bool TryFile(const Path path);
|
||||
bool TryFile(const AllocatedPath &base_path,
|
||||
PathTraitsFS::const_pointer path);
|
||||
};
|
||||
|
||||
bool ConfigLoader::TryFile(Path path)
|
||||
{
|
||||
if (FileExists(path)) {
|
||||
result = ReadConfigFile(path, error);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigLoader::TryFile(const AllocatedPath &base_path,
|
||||
PathTraitsFS::const_pointer path)
|
||||
{
|
||||
if (base_path.IsNull())
|
||||
return false;
|
||||
auto full_path = AllocatedPath::Build(base_path, path);
|
||||
return TryFile(full_path);
|
||||
}
|
||||
|
||||
bool
|
||||
parse_cmdline(int argc, char **argv, struct options *options,
|
||||
Error &error)
|
||||
{
|
||||
GOptionContext *context;
|
||||
bool ret;
|
||||
static gboolean option_version,
|
||||
option_no_daemon,
|
||||
option_no_config;
|
||||
const GOptionEntry entries[] = {
|
||||
{ "kill", 0, 0, G_OPTION_ARG_NONE, &options->kill,
|
||||
"kill the currently running mpd session", nullptr },
|
||||
{ "no-config", 0, 0, G_OPTION_ARG_NONE, &option_no_config,
|
||||
"don't read from config", nullptr },
|
||||
{ "no-daemon", 0, 0, G_OPTION_ARG_NONE, &option_no_daemon,
|
||||
"don't detach from console", nullptr },
|
||||
{ "stdout", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
|
||||
nullptr, nullptr },
|
||||
{ "stderr", 0, 0, G_OPTION_ARG_NONE, &options->log_stderr,
|
||||
"print messages to stderr", nullptr },
|
||||
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &options->verbose,
|
||||
"verbose logging", nullptr },
|
||||
{ "version", 'V', 0, G_OPTION_ARG_NONE, &option_version,
|
||||
"print version number", nullptr },
|
||||
{ nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
bool use_config_file = true;
|
||||
options->kill = false;
|
||||
options->daemon = true;
|
||||
options->log_stderr = false;
|
||||
options->verbose = false;
|
||||
|
||||
context = g_option_context_new("[path/to/mpd.conf]");
|
||||
g_option_context_add_main_entries(context, entries, nullptr);
|
||||
|
||||
g_option_context_set_summary(context, summary);
|
||||
|
||||
GError *gerror = nullptr;
|
||||
ret = g_option_context_parse(context, &argc, &argv, &gerror);
|
||||
g_option_context_free(context);
|
||||
|
||||
if (!ret)
|
||||
FatalError("option parsing failed", gerror);
|
||||
|
||||
if (option_version)
|
||||
// First pass: handle command line options
|
||||
OptionParser parser(argc, argv);
|
||||
while (parser.HasEntries()) {
|
||||
if (!parser.ParseNext())
|
||||
continue;
|
||||
if (parser.CheckOption(opt_kill)) {
|
||||
options->kill = true;
|
||||
continue;
|
||||
}
|
||||
if (parser.CheckOption(opt_no_config)) {
|
||||
use_config_file = false;
|
||||
continue;
|
||||
}
|
||||
if (parser.CheckOption(opt_no_daemon)) {
|
||||
options->daemon = false;
|
||||
continue;
|
||||
}
|
||||
if (parser.CheckOption(opt_stderr, opt_stdout)) {
|
||||
options->log_stderr = true;
|
||||
continue;
|
||||
}
|
||||
if (parser.CheckOption(opt_verbose)) {
|
||||
options->verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (parser.CheckOption(opt_version))
|
||||
version();
|
||||
if (parser.CheckOption(opt_help, opt_help_alt))
|
||||
help();
|
||||
|
||||
error.Format(cmdline_domain, "invalid option: %s",
|
||||
parser.GetOption());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* initialize the logging library, so the configuration file
|
||||
parser can use it already */
|
||||
log_early_init(options->verbose);
|
||||
|
||||
options->daemon = !option_no_daemon;
|
||||
|
||||
if (option_no_config) {
|
||||
if (!use_config_file) {
|
||||
LogDebug(cmdline_domain,
|
||||
"Ignoring config, using daemon defaults");
|
||||
return true;
|
||||
} else if (argc <= 1) {
|
||||
/* default configuration file path */
|
||||
|
||||
#ifdef WIN32
|
||||
AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()),
|
||||
CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
const char *const*system_config_dirs =
|
||||
g_get_system_config_dirs();
|
||||
|
||||
for (unsigned i = 0; system_config_dirs[i] != nullptr; ++i) {
|
||||
path = PathBuildChecked(AllocatedPath::FromUTF8(system_config_dirs[i]),
|
||||
CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
}
|
||||
#else
|
||||
AllocatedPath path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_user_config_dir()),
|
||||
USER_CONFIG_FILE_LOCATION_XDG);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_home_dir()),
|
||||
USER_CONFIG_FILE_LOCATION1);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
path = PathBuildChecked(AllocatedPath::FromUTF8(g_get_home_dir()),
|
||||
USER_CONFIG_FILE_LOCATION2);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
|
||||
path = AllocatedPath::FromUTF8(SYSTEM_CONFIG_FILE_LOCATION);
|
||||
if (!path.IsNull() && FileExists(path))
|
||||
return ReadConfigFile(path, error);
|
||||
#endif
|
||||
|
||||
error.Set(cmdline_domain, "No configuration file found");
|
||||
return false;
|
||||
} else if (argc == 2) {
|
||||
/* specified configuration file */
|
||||
return ReadConfigFile(Path::FromFS(argv[1]), error);
|
||||
} else {
|
||||
// Second pass: find non-option parameters (i.e. config file)
|
||||
const char *config_file = nullptr;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (OptionParser::IsOption(argv[i]))
|
||||
continue;
|
||||
if (config_file == nullptr) {
|
||||
config_file = argv[i];
|
||||
continue;
|
||||
}
|
||||
error.Set(cmdline_domain, "too many arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config_file != nullptr) {
|
||||
/* use specified configuration file */
|
||||
return ReadConfigFile(Path::FromFS(config_file), error);
|
||||
}
|
||||
|
||||
/* use default configuration file path */
|
||||
|
||||
ConfigLoader loader(error);
|
||||
|
||||
bool found =
|
||||
#ifdef WIN32
|
||||
loader.TryFile(GetUserConfigDir(), CONFIG_FILE_LOCATION) ||
|
||||
loader.TryFile(GetSystemConfigDir(), CONFIG_FILE_LOCATION) ||
|
||||
loader.TryFile(GetAppBaseDir(), APP_CONFIG_FILE_LOCATION);
|
||||
#else
|
||||
loader.TryFile(GetUserConfigDir(),
|
||||
USER_CONFIG_FILE_LOCATION_XDG) ||
|
||||
loader.TryFile(GetHomeDir(), USER_CONFIG_FILE_LOCATION1) ||
|
||||
loader.TryFile(GetHomeDir(), USER_CONFIG_FILE_LOCATION2) ||
|
||||
loader.TryFile(Path::FromFS(SYSTEM_CONFIG_FILE_LOCATION));
|
||||
#endif
|
||||
if (!found) {
|
||||
error.Set(cmdline_domain, "No configuration file found");
|
||||
return false;
|
||||
}
|
||||
|
||||
return loader.GetResult();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,15 +20,13 @@
|
||||
#ifndef MPD_COMMAND_LINE_HXX
|
||||
#define MPD_COMMAND_LINE_HXX
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
class Error;
|
||||
|
||||
struct options {
|
||||
gboolean kill;
|
||||
gboolean daemon;
|
||||
gboolean log_stderr;
|
||||
gboolean verbose;
|
||||
bool kill;
|
||||
bool daemon;
|
||||
bool log_stderr;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
bool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,33 +20,45 @@
|
||||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
||||
#define GCC_CHECK_VERSION(major, minor) \
|
||||
(defined(__GNUC__) && \
|
||||
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
|
||||
#define GCC_MAKE_VERSION(major, minor, patchlevel) ((major) * 10000 + (minor) * 100 + patchlevel)
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
#define GCC_VERSION GCC_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
#define GCC_VERSION 0
|
||||
#endif
|
||||
|
||||
#define GCC_CHECK_VERSION(major, minor) \
|
||||
(defined(__GNUC__) && GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
|
||||
|
||||
/**
|
||||
* Are we building with gcc (not clang or any other compiler) and a
|
||||
* version older than the specified one?
|
||||
*/
|
||||
#define GCC_OLDER_THAN(major, minor) \
|
||||
(defined(__GNUC__) && !defined(__clang__) && \
|
||||
GCC_VERSION < GCC_MAKE_VERSION(major, minor, 0))
|
||||
|
||||
#ifdef __clang__
|
||||
# define CLANG_VERSION (__clang_major__ * 10000 \
|
||||
+ __clang_minor__ * 100 \
|
||||
+ __clang_patchlevel__)
|
||||
# define CLANG_VERSION GCC_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
|
||||
# if __clang_major__ < 3
|
||||
# error Sorry, your clang version is too old. You need at least version 3.1.
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# if !GCC_CHECK_VERSION(4,6)
|
||||
# if GCC_OLDER_THAN(4,6)
|
||||
# error Sorry, your gcc version is too old. You need at least version 4.6.
|
||||
# endif
|
||||
#else
|
||||
# warning Untested compiler. Use at your own risk!
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Are we building with the specified version of clang or newer?
|
||||
*/
|
||||
#define CLANG_CHECK_VERSION(major, minor) \
|
||||
(defined(__clang__) && \
|
||||
CLANG_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
|
||||
|
||||
#if GCC_CHECK_VERSION(4,0)
|
||||
|
||||
/* GCC 4.x */
|
||||
@@ -141,7 +153,7 @@
|
||||
#if defined(__cplusplus)
|
||||
|
||||
/* support for C++11 "override" was added in gcc 4.7 */
|
||||
#if !defined(__clang__) && !GCC_CHECK_VERSION(4,7)
|
||||
#if GCC_OLDER_THAN(4,7)
|
||||
#define override
|
||||
#define final
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "CrossFade.hxx"
|
||||
#include "Chrono.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "AudioFormat.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
@@ -26,8 +27,6 @@
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static constexpr Domain cross_fade_domain("cross_fade");
|
||||
|
||||
@@ -87,7 +86,7 @@ mixramp_interpolate(const char *ramp_list, float required_db)
|
||||
}
|
||||
|
||||
unsigned
|
||||
CrossFadeSettings::Calculate(float total_time,
|
||||
CrossFadeSettings::Calculate(SignedSongTime total_time,
|
||||
float replay_gain_db, float replay_gain_prev_db,
|
||||
const char *mixramp_start, const char *mixramp_prev_end,
|
||||
const AudioFormat af,
|
||||
@@ -97,7 +96,8 @@ CrossFadeSettings::Calculate(float total_time,
|
||||
unsigned int chunks = 0;
|
||||
float chunks_f;
|
||||
|
||||
if (duration < 0 || duration >= total_time ||
|
||||
if (total_time.IsNegative() ||
|
||||
duration < 0 || duration >= total_time.ToDoubleS() ||
|
||||
/* we can't crossfade when the audio formats are different */
|
||||
af != old_format)
|
||||
return 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Compiler.h"
|
||||
|
||||
struct AudioFormat;
|
||||
class SignedSongTime;
|
||||
|
||||
struct CrossFadeSettings {
|
||||
/**
|
||||
@@ -60,7 +61,7 @@ struct CrossFadeSettings {
|
||||
* should be disabled for this song change
|
||||
*/
|
||||
gcc_pure
|
||||
unsigned Calculate(float total_time,
|
||||
unsigned Calculate(SignedSongTime total_time,
|
||||
float replay_gain_db, float replay_gain_prev_db,
|
||||
const char *mixramp_start,
|
||||
const char *mixramp_prev_end,
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabaseSimple.hxx"
|
||||
#include "DatabaseRegistry.hxx"
|
||||
#include "DatabaseSave.hxx"
|
||||
#include "DatabaseError.hxx"
|
||||
#include "Directory.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "Stats.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
#include "db/SimpleDatabasePlugin.hxx"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static Database *db;
|
||||
static bool db_is_open;
|
||||
static bool is_simple;
|
||||
|
||||
bool
|
||||
DatabaseGlobalInit(const config_param ¶m, Error &error)
|
||||
{
|
||||
assert(db == nullptr);
|
||||
assert(!db_is_open);
|
||||
|
||||
const char *plugin_name =
|
||||
param.GetBlockValue("plugin", "simple");
|
||||
is_simple = strcmp(plugin_name, "simple") == 0;
|
||||
|
||||
const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
|
||||
if (plugin == nullptr) {
|
||||
error.Format(db_domain,
|
||||
"No such database plugin: %s", plugin_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
db = plugin->create(param, error);
|
||||
return db != nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseGlobalDeinit(void)
|
||||
{
|
||||
if (db_is_open)
|
||||
db->Close();
|
||||
|
||||
if (db != nullptr)
|
||||
delete db;
|
||||
}
|
||||
|
||||
const Database *
|
||||
GetDatabase()
|
||||
{
|
||||
assert(db == nullptr || db_is_open);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
const Database *
|
||||
GetDatabase(Error &error)
|
||||
{
|
||||
assert(db == nullptr || db_is_open);
|
||||
|
||||
if (db == nullptr)
|
||||
error.Set(db_domain, DB_DISABLED, "No database");
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
bool
|
||||
db_is_simple(void)
|
||||
{
|
||||
assert(db == nullptr || db_is_open);
|
||||
|
||||
return is_simple;
|
||||
}
|
||||
|
||||
Directory *
|
||||
db_get_root(void)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->GetRoot();
|
||||
}
|
||||
|
||||
Directory *
|
||||
db_get_directory(const char *name)
|
||||
{
|
||||
if (db == nullptr)
|
||||
return nullptr;
|
||||
|
||||
Directory *music_root = db_get_root();
|
||||
if (name == nullptr)
|
||||
return music_root;
|
||||
|
||||
return music_root->LookupDirectory(name);
|
||||
}
|
||||
|
||||
bool
|
||||
db_save(Error &error)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_open);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->Save(error);
|
||||
}
|
||||
|
||||
bool
|
||||
DatabaseGlobalOpen(Error &error)
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(!db_is_open);
|
||||
|
||||
if (!db->Open(error))
|
||||
return false;
|
||||
|
||||
db_is_open = true;
|
||||
|
||||
stats_update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
db_exists()
|
||||
{
|
||||
assert(db != nullptr);
|
||||
assert(db_is_open);
|
||||
assert(db_is_simple());
|
||||
|
||||
return ((SimpleDatabase *)db)->GetUpdateStamp() > 0;
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DatabasePrint.hxx"
|
||||
#include "DatabaseSelection.hxx"
|
||||
#include "SongFilter.hxx"
|
||||
#include "PlaylistVector.hxx"
|
||||
#include "SongPrint.hxx"
|
||||
#include "TimePrint.hxx"
|
||||
#include "Directory.hxx"
|
||||
#include "Client.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabasePlugin.hxx"
|
||||
|
||||
#include <functional>
|
||||
|
||||
static bool
|
||||
PrintDirectoryBrief(Client &client, const Directory &directory)
|
||||
{
|
||||
if (!directory.IsRoot())
|
||||
client_printf(client, "directory: %s\n", directory.GetPath());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintDirectoryFull(Client &client, const Directory &directory)
|
||||
{
|
||||
if (!directory.IsRoot()) {
|
||||
client_printf(client, "directory: %s\n", directory.GetPath());
|
||||
time_print(client, "Last-Modified", directory.mtime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_playlist_in_directory(Client &client,
|
||||
const Directory &directory,
|
||||
const char *name_utf8)
|
||||
{
|
||||
if (directory.IsRoot())
|
||||
client_printf(client, "playlist: %s\n", name_utf8);
|
||||
else
|
||||
client_printf(client, "playlist: %s/%s\n",
|
||||
directory.GetPath(), name_utf8);
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintSongBrief(Client &client, const Song &song)
|
||||
{
|
||||
assert(song.parent != nullptr);
|
||||
|
||||
song_print_uri(client, song);
|
||||
|
||||
if (song.tag != nullptr && song.tag->has_playlist)
|
||||
/* this song file has an embedded CUE sheet */
|
||||
print_playlist_in_directory(client, *song.parent, song.uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintSongFull(Client &client, const Song &song)
|
||||
{
|
||||
assert(song.parent != nullptr);
|
||||
|
||||
song_print_info(client, song);
|
||||
|
||||
if (song.tag != nullptr && song.tag->has_playlist)
|
||||
/* this song file has an embedded CUE sheet */
|
||||
print_playlist_in_directory(client, *song.parent, song.uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintPlaylistBrief(Client &client,
|
||||
const PlaylistInfo &playlist,
|
||||
const Directory &directory)
|
||||
{
|
||||
print_playlist_in_directory(client, directory, playlist.name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintPlaylistFull(Client &client,
|
||||
const PlaylistInfo &playlist,
|
||||
const Directory &directory)
|
||||
{
|
||||
print_playlist_in_directory(client, directory, playlist.name.c_str());
|
||||
|
||||
if (playlist.mtime > 0)
|
||||
time_print(client, "Last-Modified", playlist.mtime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
db_selection_print(Client &client, const DatabaseSelection &selection,
|
||||
bool full, Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
using namespace std::placeholders;
|
||||
const auto d = selection.filter == nullptr
|
||||
? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief,
|
||||
std::ref(client), _1)
|
||||
: VisitDirectory();
|
||||
const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
|
||||
std::ref(client), _1);
|
||||
const auto p = selection.filter == nullptr
|
||||
? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
|
||||
std::ref(client), _1, _2)
|
||||
: VisitPlaylist();
|
||||
|
||||
return db->Visit(selection, d, s, p, error);
|
||||
}
|
||||
|
||||
struct SearchStats {
|
||||
int numberOfSongs;
|
||||
unsigned long playTime;
|
||||
};
|
||||
|
||||
static void printSearchStats(Client &client, SearchStats *stats)
|
||||
{
|
||||
client_printf(client, "songs: %i\n", stats->numberOfSongs);
|
||||
client_printf(client, "playtime: %li\n", stats->playTime);
|
||||
}
|
||||
|
||||
static bool
|
||||
stats_visitor_song(SearchStats &stats, Song &song)
|
||||
{
|
||||
stats.numberOfSongs++;
|
||||
stats.playTime += song.GetDuration();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
searchStatsForSongsIn(Client &client, const char *name,
|
||||
const SongFilter *filter,
|
||||
Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
const DatabaseSelection selection(name, true, filter);
|
||||
|
||||
SearchStats stats;
|
||||
stats.numberOfSongs = 0;
|
||||
stats.playTime = 0;
|
||||
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(stats_visitor_song, std::ref(stats),
|
||||
_1);
|
||||
if (!db->Visit(selection, f, error))
|
||||
return false;
|
||||
|
||||
printSearchStats(client, &stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
printAllIn(Client &client, const char *uri_utf8, Error &error)
|
||||
{
|
||||
const DatabaseSelection selection(uri_utf8, true);
|
||||
return db_selection_print(client, selection, false, error);
|
||||
}
|
||||
|
||||
bool
|
||||
printInfoForAllIn(Client &client, const char *uri_utf8,
|
||||
Error &error)
|
||||
{
|
||||
const DatabaseSelection selection(uri_utf8, true);
|
||||
return db_selection_print(client, selection, true, error);
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintSongURIVisitor(Client &client, Song &song)
|
||||
{
|
||||
song_print_uri(client, song);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
PrintUniqueTag(Client &client, TagType tag_type,
|
||||
const char *value)
|
||||
{
|
||||
client_printf(client, "%s: %s\n", tag_item_names[tag_type], value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
listAllUniqueTags(Client &client, int type,
|
||||
const SongFilter *filter,
|
||||
Error &error)
|
||||
{
|
||||
const Database *db = GetDatabase(error);
|
||||
if (db == nullptr)
|
||||
return false;
|
||||
|
||||
const DatabaseSelection selection("", true, filter);
|
||||
|
||||
if (type == LOCATE_TAG_FILE_TYPE) {
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(PrintSongURIVisitor,
|
||||
std::ref(client), _1);
|
||||
return db->Visit(selection, f, error);
|
||||
} else {
|
||||
using namespace std::placeholders;
|
||||
const auto f = std::bind(PrintUniqueTag, std::ref(client),
|
||||
(TagType)type, _1);
|
||||
return db->VisitUniqueTags(selection, (TagType)type,
|
||||
f, error);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DATABASE_SIMPLE_HXX
|
||||
#define MPD_DATABASE_SIMPLE_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
struct config_param;
|
||||
struct Directory;
|
||||
struct db_selection;
|
||||
struct db_visitor;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Check whether the default #SimpleDatabasePlugin is used. This
|
||||
* allows using db_get_root(), db_save(), db_get_mtime() and
|
||||
* db_exists().
|
||||
*/
|
||||
bool
|
||||
db_is_simple(void);
|
||||
|
||||
/**
|
||||
* Returns the root directory object. Returns NULL if there is no
|
||||
* configured music directory.
|
||||
*
|
||||
* May only be used if db_is_simple() returns true.
|
||||
*/
|
||||
gcc_pure
|
||||
Directory *
|
||||
db_get_root(void);
|
||||
|
||||
/**
|
||||
* Caller must lock the #db_mutex.
|
||||
*/
|
||||
gcc_nonnull(1)
|
||||
gcc_pure
|
||||
Directory *
|
||||
db_get_directory(const char *name);
|
||||
|
||||
/**
|
||||
* May only be used if db_is_simple() returns true.
|
||||
*/
|
||||
bool
|
||||
db_save(Error &error);
|
||||
|
||||
/**
|
||||
* Returns true if there is a valid database file on the disk.
|
||||
*
|
||||
* May only be used if db_is_simple() returns true.
|
||||
*/
|
||||
gcc_pure
|
||||
bool
|
||||
db_exists();
|
||||
|
||||
#endif
|
||||
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DecoderBuffer.hxx"
|
||||
#include "DecoderAPI.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
struct DecoderBuffer {
|
||||
Decoder *decoder;
|
||||
InputStream *is;
|
||||
|
||||
/** the allocated size of the buffer */
|
||||
size_t size;
|
||||
|
||||
/** the current length of the buffer */
|
||||
size_t length;
|
||||
|
||||
/** number of bytes already consumed at the beginning of the
|
||||
buffer */
|
||||
size_t consumed;
|
||||
|
||||
/** the actual buffer (dynamic size) */
|
||||
unsigned char data[sizeof(size_t)];
|
||||
};
|
||||
|
||||
DecoderBuffer *
|
||||
decoder_buffer_new(Decoder *decoder, InputStream &is,
|
||||
size_t size)
|
||||
{
|
||||
DecoderBuffer *buffer = (DecoderBuffer *)
|
||||
g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size);
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
buffer->decoder = decoder;
|
||||
buffer->is = &is;
|
||||
buffer->size = size;
|
||||
buffer->length = 0;
|
||||
buffer->consumed = 0;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void
|
||||
decoder_buffer_free(DecoderBuffer *buffer)
|
||||
{
|
||||
assert(buffer != nullptr);
|
||||
|
||||
g_free(buffer);
|
||||
}
|
||||
|
||||
const InputStream &
|
||||
decoder_buffer_get_stream(const DecoderBuffer *buffer)
|
||||
{
|
||||
return *buffer->is;
|
||||
}
|
||||
|
||||
bool
|
||||
decoder_buffer_is_empty(const DecoderBuffer *buffer)
|
||||
{
|
||||
return buffer->consumed == buffer->length;
|
||||
}
|
||||
|
||||
bool
|
||||
decoder_buffer_is_full(const DecoderBuffer *buffer)
|
||||
{
|
||||
return buffer->consumed == 0 && buffer->length == buffer->size;
|
||||
}
|
||||
|
||||
void
|
||||
decoder_buffer_clear(DecoderBuffer *buffer)
|
||||
{
|
||||
buffer->length = buffer->consumed = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
decoder_buffer_shift(DecoderBuffer *buffer)
|
||||
{
|
||||
assert(buffer->consumed > 0);
|
||||
|
||||
buffer->length -= buffer->consumed;
|
||||
memmove(buffer->data, buffer->data + buffer->consumed, buffer->length);
|
||||
buffer->consumed = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
decoder_buffer_fill(DecoderBuffer *buffer)
|
||||
{
|
||||
size_t nbytes;
|
||||
|
||||
if (buffer->consumed > 0)
|
||||
decoder_buffer_shift(buffer);
|
||||
|
||||
if (buffer->length >= buffer->size)
|
||||
/* buffer is full */
|
||||
return false;
|
||||
|
||||
nbytes = decoder_read(buffer->decoder, *buffer->is,
|
||||
buffer->data + buffer->length,
|
||||
buffer->size - buffer->length);
|
||||
if (nbytes == 0)
|
||||
/* end of file, I/O error or decoder command
|
||||
received */
|
||||
return false;
|
||||
|
||||
buffer->length += nbytes;
|
||||
assert(buffer->length <= buffer->size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
decoder_buffer_available(const DecoderBuffer *buffer)
|
||||
{
|
||||
return buffer->length - buffer->consumed;;
|
||||
}
|
||||
|
||||
const void *
|
||||
decoder_buffer_read(const DecoderBuffer *buffer, size_t *length_r)
|
||||
{
|
||||
if (buffer->consumed >= buffer->length)
|
||||
/* buffer is empty */
|
||||
return nullptr;
|
||||
|
||||
*length_r = buffer->length - buffer->consumed;
|
||||
return buffer->data + buffer->consumed;
|
||||
}
|
||||
|
||||
void
|
||||
decoder_buffer_consume(DecoderBuffer *buffer, size_t nbytes)
|
||||
{
|
||||
/* just move the "consumed" pointer - decoder_buffer_shift()
|
||||
will do the real work later (called by
|
||||
decoder_buffer_fill()) */
|
||||
buffer->consumed += nbytes;
|
||||
|
||||
assert(buffer->consumed <= buffer->length);
|
||||
}
|
||||
|
||||
bool
|
||||
decoder_buffer_skip(DecoderBuffer *buffer, size_t nbytes)
|
||||
{
|
||||
size_t length;
|
||||
const void *data;
|
||||
bool success;
|
||||
|
||||
/* this could probably be optimized by seeking */
|
||||
|
||||
while (true) {
|
||||
data = decoder_buffer_read(buffer, &length);
|
||||
if (data != nullptr) {
|
||||
if (length > nbytes)
|
||||
length = nbytes;
|
||||
decoder_buffer_consume(buffer, length);
|
||||
nbytes -= length;
|
||||
if (nbytes == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
success = decoder_buffer_fill(buffer);
|
||||
if (!success)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DECODER_BUFFER_HXX
|
||||
#define MPD_DECODER_BUFFER_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* This objects handles buffered reads in decoder plugins easily. You
|
||||
* create a buffer object, and use its high-level methods to fill and
|
||||
* read it. It will automatically handle shifting the buffer.
|
||||
*/
|
||||
struct DecoderBuffer;
|
||||
|
||||
struct Decoder;
|
||||
struct InputStream;
|
||||
|
||||
/**
|
||||
* Creates a new buffer.
|
||||
*
|
||||
* @param decoder the decoder object, used for decoder_read(), may be nullptr
|
||||
* @param is the input stream object where we should read from
|
||||
* @param size the maximum size of the buffer
|
||||
* @return the new decoder_buffer object
|
||||
*/
|
||||
DecoderBuffer *
|
||||
decoder_buffer_new(Decoder *decoder, InputStream &is,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* Frees resources used by the decoder_buffer object.
|
||||
*/
|
||||
void
|
||||
decoder_buffer_free(DecoderBuffer *buffer);
|
||||
|
||||
gcc_pure
|
||||
const InputStream &
|
||||
decoder_buffer_get_stream(const DecoderBuffer *buffer);
|
||||
|
||||
gcc_pure
|
||||
bool
|
||||
decoder_buffer_is_empty(const DecoderBuffer *buffer);
|
||||
|
||||
gcc_pure
|
||||
bool
|
||||
decoder_buffer_is_full(const DecoderBuffer *buffer);
|
||||
|
||||
void
|
||||
decoder_buffer_clear(DecoderBuffer *buffer);
|
||||
|
||||
/**
|
||||
* Read data from the input_stream and append it to the buffer.
|
||||
*
|
||||
* @return true if data was appended; false if there is no data
|
||||
* available (yet), end of file, I/O error or a decoder command was
|
||||
* received
|
||||
*/
|
||||
bool
|
||||
decoder_buffer_fill(DecoderBuffer *buffer);
|
||||
|
||||
/**
|
||||
* How many bytes are stored in the buffer?
|
||||
*/
|
||||
gcc_pure
|
||||
size_t
|
||||
decoder_buffer_available(const DecoderBuffer *buffer);
|
||||
|
||||
/**
|
||||
* Reads data from the buffer. This data is not yet consumed, you
|
||||
* have to call decoder_buffer_consume() to do that. The returned
|
||||
* buffer becomes invalid after a decoder_buffer_fill() or a
|
||||
* decoder_buffer_consume() call.
|
||||
*
|
||||
* @param buffer the decoder_buffer object
|
||||
* @param length_r pointer to a size_t where you will receive the
|
||||
* number of bytes available
|
||||
* @return a pointer to the read buffer, or nullptr if there is no data
|
||||
* available
|
||||
*/
|
||||
const void *
|
||||
decoder_buffer_read(const DecoderBuffer *buffer, size_t *length_r);
|
||||
|
||||
/**
|
||||
* Consume (delete, invalidate) a part of the buffer. The "nbytes"
|
||||
* parameter must not be larger than the length returned by
|
||||
* decoder_buffer_read().
|
||||
*
|
||||
* @param buffer the decoder_buffer object
|
||||
* @param nbytes the number of bytes to consume
|
||||
*/
|
||||
void
|
||||
decoder_buffer_consume(DecoderBuffer *buffer, size_t nbytes);
|
||||
|
||||
/**
|
||||
* Skips the specified number of bytes, discarding its data.
|
||||
*
|
||||
* @param buffer the decoder_buffer object
|
||||
* @param nbytes the number of bytes to skip
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
bool
|
||||
decoder_buffer_skip(DecoderBuffer *buffer, size_t nbytes);
|
||||
|
||||
#endif
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "DespotifyUtils.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
extern "C" {
|
||||
#include <despotify.h>
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
const Domain despotify_domain("despotify");
|
||||
|
||||
static struct despotify_session *g_session;
|
||||
static void (*registered_callbacks[8])(struct despotify_session *,
|
||||
int, void *, void *);
|
||||
static void *registered_callback_data[8];
|
||||
|
||||
static void
|
||||
callback(struct despotify_session* ds, int sig,
|
||||
void *data, gcc_unused void *callback_data)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
|
||||
void (*cb)(struct despotify_session *, int, void *, void *) = registered_callbacks[i];
|
||||
void *cb_data = registered_callback_data[i];
|
||||
|
||||
if (cb)
|
||||
cb(ds, sig, data, cb_data);
|
||||
}
|
||||
}
|
||||
|
||||
bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
|
||||
void *cb_data)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
|
||||
|
||||
if (!registered_callbacks[i]) {
|
||||
registered_callbacks[i] = cb;
|
||||
registered_callback_data[i] = cb_data;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *))
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
|
||||
|
||||
if (registered_callbacks[i] == cb) {
|
||||
registered_callbacks[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Tag *
|
||||
mpd_despotify_tag_from_track(struct ds_track *track)
|
||||
{
|
||||
char tracknum[20];
|
||||
char comment[80];
|
||||
char date[20];
|
||||
|
||||
Tag *tag = new Tag();
|
||||
|
||||
if (!track->has_meta_data)
|
||||
return tag;
|
||||
|
||||
snprintf(tracknum, sizeof(tracknum), "%d", track->tracknumber);
|
||||
snprintf(date, sizeof(date), "%d", track->year);
|
||||
snprintf(comment, sizeof(comment), "Bitrate %d Kbps, %sgeo restricted",
|
||||
track->file_bitrate / 1000,
|
||||
track->geo_restricted ? "" : "not ");
|
||||
tag->AddItem(TAG_TITLE, track->title);
|
||||
tag->AddItem(TAG_ARTIST, track->artist->name);
|
||||
tag->AddItem(TAG_TRACK, tracknum);
|
||||
tag->AddItem(TAG_ALBUM, track->album);
|
||||
tag->AddItem(TAG_DATE, date);
|
||||
tag->AddItem(TAG_COMMENT, comment);
|
||||
tag->time = track->length / 1000;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
struct despotify_session *mpd_despotify_get_session(void)
|
||||
{
|
||||
const char *user;
|
||||
const char *passwd;
|
||||
bool high_bitrate;
|
||||
|
||||
if (g_session)
|
||||
return g_session;
|
||||
|
||||
user = config_get_string(CONF_DESPOTIFY_USER, nullptr);
|
||||
passwd = config_get_string(CONF_DESPOTIFY_PASSWORD, nullptr);
|
||||
high_bitrate = config_get_bool(CONF_DESPOTIFY_HIGH_BITRATE, true);
|
||||
|
||||
if (user == nullptr || passwd == nullptr) {
|
||||
LogDebug(despotify_domain,
|
||||
"disabling despotify because account is not configured");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!despotify_init()) {
|
||||
LogWarning(despotify_domain, "Can't initialize despotify");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
g_session = despotify_init_client(callback, nullptr,
|
||||
high_bitrate, true);
|
||||
if (!g_session) {
|
||||
LogWarning(despotify_domain,
|
||||
"Can't initialize despotify client");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!despotify_authenticate(g_session, user, passwd)) {
|
||||
LogWarning(despotify_domain,
|
||||
"Can't authenticate despotify session");
|
||||
despotify_exit(g_session);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return g_session;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DESPOTIFY_H
|
||||
#define MPD_DESPOTIFY_H
|
||||
|
||||
struct Tag;
|
||||
struct despotify_session;
|
||||
struct ds_track;
|
||||
|
||||
extern const class Domain despotify_domain;
|
||||
|
||||
/**
|
||||
* Return the current despotify session.
|
||||
*
|
||||
* If the session isn't initialized, this function will initialize
|
||||
* it and connect to Spotify.
|
||||
*
|
||||
* @return a pointer to the despotify session, or nullptr if it can't
|
||||
* be initialized (e.g., if the configuration isn't supplied)
|
||||
*/
|
||||
struct despotify_session *mpd_despotify_get_session(void);
|
||||
|
||||
/**
|
||||
* Create a MPD tags structure from a spotify track
|
||||
*
|
||||
* @param track the track to convert
|
||||
*
|
||||
* @return a pointer to the filled in tags structure
|
||||
*/
|
||||
Tag *
|
||||
mpd_despotify_tag_from_track(struct ds_track *track);
|
||||
|
||||
/**
|
||||
* Register a despotify callback.
|
||||
*
|
||||
* Despotify calls this e.g., when a track ends.
|
||||
*
|
||||
* @param cb the callback
|
||||
* @param cb_data the data to pass to the callback
|
||||
*
|
||||
* @return true if the callback could be registered
|
||||
*/
|
||||
bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
|
||||
void *cb_data);
|
||||
|
||||
/**
|
||||
* Unregister a despotify callback.
|
||||
*
|
||||
* @param cb the callback to unregister.
|
||||
*/
|
||||
void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *));
|
||||
|
||||
#endif
|
||||
|
||||
73
src/DetachedSong.cxx
Normal file
73
src/DetachedSong.cxx
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "DetachedSong.hxx"
|
||||
#include "db/LightSong.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
|
||||
DetachedSong::DetachedSong(const LightSong &other)
|
||||
:uri(other.GetURI().c_str()),
|
||||
real_uri(other.real_uri != nullptr ? other.real_uri : ""),
|
||||
tag(*other.tag),
|
||||
mtime(other.mtime),
|
||||
start_time(other.start_time),
|
||||
end_time(other.end_time) {}
|
||||
|
||||
DetachedSong::~DetachedSong()
|
||||
{
|
||||
/* this destructor exists here just so it won't inlined */
|
||||
}
|
||||
|
||||
bool
|
||||
DetachedSong::IsRemote() const
|
||||
{
|
||||
return uri_has_scheme(GetRealURI());
|
||||
}
|
||||
|
||||
bool
|
||||
DetachedSong::IsAbsoluteFile() const
|
||||
{
|
||||
return PathTraitsUTF8::IsAbsolute(GetRealURI());
|
||||
}
|
||||
|
||||
bool
|
||||
DetachedSong::IsInDatabase() const
|
||||
{
|
||||
/* here, we use GetURI() and not GetRealURI() because
|
||||
GetRealURI() is never relative */
|
||||
|
||||
const char *_uri = GetURI();
|
||||
return !uri_has_scheme(_uri) && !PathTraitsUTF8::IsAbsolute(_uri);
|
||||
}
|
||||
|
||||
SignedSongTime
|
||||
DetachedSong::GetDuration() const
|
||||
{
|
||||
SongTime a = start_time, b = end_time;
|
||||
if (!b.IsPositive()) {
|
||||
if (tag.duration.IsNegative())
|
||||
return tag.duration;
|
||||
|
||||
b = SongTime(tag.duration);
|
||||
}
|
||||
|
||||
return SignedSongTime(b - a);
|
||||
}
|
||||
234
src/DetachedSong.hxx
Normal file
234
src/DetachedSong.hxx
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_DETACHED_SONG_HXX
|
||||
#define MPD_DETACHED_SONG_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "Chrono.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct LightSong;
|
||||
class Storage;
|
||||
|
||||
class DetachedSong {
|
||||
friend DetachedSong map_song_detach(const LightSong &song);
|
||||
friend DetachedSong DatabaseDetachSong(const Storage &db,
|
||||
const LightSong &song);
|
||||
|
||||
/**
|
||||
* An UTF-8-encoded URI referring to the song file. This can
|
||||
* be one of:
|
||||
*
|
||||
* - an absolute URL with a scheme
|
||||
* (e.g. "http://example.com/foo.mp3")
|
||||
*
|
||||
* - an absolute file name
|
||||
*
|
||||
* - a file name relative to the music directory
|
||||
*/
|
||||
std::string uri;
|
||||
|
||||
/**
|
||||
* The "real" URI, the one to be used for opening the
|
||||
* resource. If this attribute is empty, then #uri shall be
|
||||
* used.
|
||||
*
|
||||
* This attribute is used for songs from the database which
|
||||
* have a relative URI.
|
||||
*/
|
||||
std::string real_uri;
|
||||
|
||||
Tag tag;
|
||||
|
||||
time_t mtime;
|
||||
|
||||
/**
|
||||
* Start of this sub-song within the file.
|
||||
*/
|
||||
SongTime start_time;
|
||||
|
||||
/**
|
||||
* End of this sub-song within the file.
|
||||
* Unused if zero.
|
||||
*/
|
||||
SongTime end_time;
|
||||
|
||||
explicit DetachedSong(const LightSong &other);
|
||||
|
||||
public:
|
||||
explicit DetachedSong(const DetachedSong &) = default;
|
||||
|
||||
explicit DetachedSong(const char *_uri)
|
||||
:uri(_uri),
|
||||
mtime(0),
|
||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
||||
|
||||
explicit DetachedSong(const std::string &_uri)
|
||||
:uri(_uri),
|
||||
mtime(0),
|
||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
||||
|
||||
explicit DetachedSong(std::string &&_uri)
|
||||
:uri(std::move(_uri)),
|
||||
mtime(0),
|
||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
||||
|
||||
template<typename U>
|
||||
DetachedSong(U &&_uri, Tag &&_tag)
|
||||
:uri(std::forward<U>(_uri)),
|
||||
tag(std::move(_tag)),
|
||||
mtime(0),
|
||||
start_time(SongTime::zero()), end_time(SongTime::zero()) {}
|
||||
|
||||
DetachedSong(DetachedSong &&) = default;
|
||||
|
||||
~DetachedSong();
|
||||
|
||||
gcc_pure
|
||||
const char *GetURI() const {
|
||||
return uri.c_str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SetURI(T &&_uri) {
|
||||
uri = std::forward<T>(_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this object have a "real" URI different from the
|
||||
* displayed URI?
|
||||
*/
|
||||
gcc_pure
|
||||
bool HasRealURI() const {
|
||||
return !real_uri.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "real" URI (#real_uri) and falls back to just
|
||||
* GetURI().
|
||||
*/
|
||||
gcc_pure
|
||||
const char *GetRealURI() const {
|
||||
return (HasRealURI() ? real_uri : uri).c_str();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SetRealURI(T &&_uri) {
|
||||
real_uri = std::forward<T>(_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if both objects refer to the same physical
|
||||
* song.
|
||||
*/
|
||||
gcc_pure
|
||||
bool IsSame(const DetachedSong &other) const {
|
||||
return uri == other.uri;
|
||||
}
|
||||
|
||||
gcc_pure gcc_nonnull_all
|
||||
bool IsURI(const char *other_uri) const {
|
||||
return uri == other_uri;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool IsRemote() const;
|
||||
|
||||
gcc_pure
|
||||
bool IsFile() const {
|
||||
return !IsRemote();
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool IsAbsoluteFile() const;
|
||||
|
||||
gcc_pure
|
||||
bool IsInDatabase() const;
|
||||
|
||||
const Tag &GetTag() const {
|
||||
return tag;
|
||||
}
|
||||
|
||||
Tag &WritableTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
void SetTag(const Tag &_tag) {
|
||||
tag = Tag(_tag);
|
||||
}
|
||||
|
||||
void SetTag(Tag &&_tag) {
|
||||
tag = std::move(_tag);
|
||||
}
|
||||
|
||||
void MoveTagFrom(DetachedSong &&other) {
|
||||
tag = std::move(other.tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the MoveTagFrom(), but move only the #TagItem
|
||||
* array.
|
||||
*/
|
||||
void MoveTagItemsFrom(DetachedSong &&other) {
|
||||
tag.MoveItemsFrom(std::move(other.tag));
|
||||
}
|
||||
|
||||
time_t GetLastModified() const {
|
||||
return mtime;
|
||||
}
|
||||
|
||||
void SetLastModified(time_t _value) {
|
||||
mtime = _value;
|
||||
}
|
||||
|
||||
SongTime GetStartTime() const {
|
||||
return start_time;
|
||||
}
|
||||
|
||||
void SetStartTime(SongTime _value) {
|
||||
start_time = _value;
|
||||
}
|
||||
|
||||
SongTime GetEndTime() const {
|
||||
return end_time;
|
||||
}
|
||||
|
||||
void SetEndTime(SongTime _value) {
|
||||
end_time = _value;
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
SignedSongTime GetDuration() const;
|
||||
|
||||
/**
|
||||
* Update the #tag and #mtime.
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool Update();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,331 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "Directory.hxx"
|
||||
#include "SongFilter.hxx"
|
||||
#include "PlaylistVector.hxx"
|
||||
#include "DatabaseLock.hxx"
|
||||
#include "SongSort.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
extern "C" {
|
||||
#include "util/list_sort.h"
|
||||
}
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
inline Directory *
|
||||
Directory::Allocate(const char *path)
|
||||
{
|
||||
assert(path != nullptr);
|
||||
|
||||
const size_t path_size = strlen(path) + 1;
|
||||
Directory *directory =
|
||||
(Directory *)g_malloc0(sizeof(*directory)
|
||||
- sizeof(directory->path)
|
||||
+ path_size);
|
||||
new(directory) Directory(path);
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
Directory::Directory()
|
||||
{
|
||||
INIT_LIST_HEAD(&children);
|
||||
INIT_LIST_HEAD(&songs);
|
||||
|
||||
path[0] = 0;
|
||||
}
|
||||
|
||||
Directory::Directory(const char *_path)
|
||||
{
|
||||
INIT_LIST_HEAD(&children);
|
||||
INIT_LIST_HEAD(&songs);
|
||||
|
||||
strcpy(path, _path);
|
||||
}
|
||||
|
||||
Directory::~Directory()
|
||||
{
|
||||
Song *song, *ns;
|
||||
directory_for_each_song_safe(song, ns, *this)
|
||||
song->Free();
|
||||
|
||||
Directory *child, *n;
|
||||
directory_for_each_child_safe(child, n, *this)
|
||||
child->Free();
|
||||
}
|
||||
|
||||
Directory *
|
||||
Directory::NewGeneric(const char *path, Directory *parent)
|
||||
{
|
||||
assert(path != nullptr);
|
||||
assert((*path == 0) == (parent == nullptr));
|
||||
|
||||
Directory *directory = Allocate(path);
|
||||
|
||||
directory->parent = parent;
|
||||
|
||||
return directory;
|
||||
}
|
||||
|
||||
void
|
||||
Directory::Free()
|
||||
{
|
||||
this->Directory::~Directory();
|
||||
g_free(this);
|
||||
}
|
||||
|
||||
void
|
||||
Directory::Delete()
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(parent != nullptr);
|
||||
|
||||
list_del(&siblings);
|
||||
Free();
|
||||
}
|
||||
|
||||
const char *
|
||||
Directory::GetName() const
|
||||
{
|
||||
assert(!IsRoot());
|
||||
|
||||
return PathTraits::GetBaseUTF8(path);
|
||||
}
|
||||
|
||||
Directory *
|
||||
Directory::CreateChild(const char *name_utf8)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(name_utf8 != nullptr);
|
||||
assert(*name_utf8 != 0);
|
||||
|
||||
char *allocated;
|
||||
const char *path_utf8;
|
||||
if (IsRoot()) {
|
||||
allocated = nullptr;
|
||||
path_utf8 = name_utf8;
|
||||
} else {
|
||||
allocated = g_strconcat(GetPath(),
|
||||
"/", name_utf8, nullptr);
|
||||
path_utf8 = allocated;
|
||||
}
|
||||
|
||||
Directory *child = NewGeneric(path_utf8, this);
|
||||
g_free(allocated);
|
||||
|
||||
list_add_tail(&child->siblings, &children);
|
||||
return child;
|
||||
}
|
||||
|
||||
const Directory *
|
||||
Directory::FindChild(const char *name) const
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
|
||||
const Directory *child;
|
||||
directory_for_each_child(child, *this)
|
||||
if (strcmp(child->GetName(), name) == 0)
|
||||
return child;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Directory::PruneEmpty()
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
|
||||
Directory *child, *n;
|
||||
directory_for_each_child_safe(child, n, *this) {
|
||||
child->PruneEmpty();
|
||||
|
||||
if (child->IsEmpty())
|
||||
child->Delete();
|
||||
}
|
||||
}
|
||||
|
||||
Directory *
|
||||
Directory::LookupDirectory(const char *uri)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(uri != nullptr);
|
||||
|
||||
if (isRootDirectory(uri))
|
||||
return this;
|
||||
|
||||
char *duplicated = g_strdup(uri), *name = duplicated;
|
||||
|
||||
Directory *d = this;
|
||||
while (1) {
|
||||
char *slash = strchr(name, '/');
|
||||
if (slash == name) {
|
||||
d = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (slash != nullptr)
|
||||
*slash = '\0';
|
||||
|
||||
d = d->FindChild(name);
|
||||
if (d == nullptr || slash == nullptr)
|
||||
break;
|
||||
|
||||
name = slash + 1;
|
||||
}
|
||||
|
||||
g_free(duplicated);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
Directory::AddSong(Song *song)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(song != nullptr);
|
||||
assert(song->parent == this);
|
||||
|
||||
list_add_tail(&song->siblings, &songs);
|
||||
}
|
||||
|
||||
void
|
||||
Directory::RemoveSong(Song *song)
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(song != nullptr);
|
||||
assert(song->parent == this);
|
||||
|
||||
list_del(&song->siblings);
|
||||
}
|
||||
|
||||
const Song *
|
||||
Directory::FindSong(const char *name_utf8) const
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
assert(name_utf8 != nullptr);
|
||||
|
||||
Song *song;
|
||||
directory_for_each_song(song, *this) {
|
||||
assert(song->parent == this);
|
||||
|
||||
if (strcmp(song->uri, name_utf8) == 0)
|
||||
return song;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Song *
|
||||
Directory::LookupSong(const char *uri)
|
||||
{
|
||||
char *duplicated, *base;
|
||||
|
||||
assert(holding_db_lock());
|
||||
assert(uri != nullptr);
|
||||
|
||||
duplicated = g_strdup(uri);
|
||||
base = strrchr(duplicated, '/');
|
||||
|
||||
Directory *d = this;
|
||||
if (base != nullptr) {
|
||||
*base++ = 0;
|
||||
d = d->LookupDirectory(duplicated);
|
||||
if (d == nullptr) {
|
||||
g_free(duplicated);
|
||||
return nullptr;
|
||||
}
|
||||
} else
|
||||
base = duplicated;
|
||||
|
||||
Song *song = d->FindSong(base);
|
||||
assert(song == nullptr || song->parent == d);
|
||||
|
||||
g_free(duplicated);
|
||||
return song;
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
directory_cmp(gcc_unused void *priv,
|
||||
struct list_head *_a, struct list_head *_b)
|
||||
{
|
||||
const Directory *a = (const Directory *)_a;
|
||||
const Directory *b = (const Directory *)_b;
|
||||
return g_utf8_collate(a->path, b->path);
|
||||
}
|
||||
|
||||
void
|
||||
Directory::Sort()
|
||||
{
|
||||
assert(holding_db_lock());
|
||||
|
||||
list_sort(nullptr, &children, directory_cmp);
|
||||
song_list_sort(&songs);
|
||||
|
||||
Directory *child;
|
||||
directory_for_each_child(child, *this)
|
||||
child->Sort();
|
||||
}
|
||||
|
||||
bool
|
||||
Directory::Walk(bool recursive, const SongFilter *filter,
|
||||
VisitDirectory visit_directory, VisitSong visit_song,
|
||||
VisitPlaylist visit_playlist,
|
||||
Error &error) const
|
||||
{
|
||||
assert(!error.IsDefined());
|
||||
|
||||
if (visit_song) {
|
||||
Song *song;
|
||||
directory_for_each_song(song, *this)
|
||||
if ((filter == nullptr || filter->Match(*song)) &&
|
||||
!visit_song(*song, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (visit_playlist) {
|
||||
for (const PlaylistInfo &p : playlists)
|
||||
if (!visit_playlist(p, *this, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
Directory *child;
|
||||
directory_for_each_child(child, *this) {
|
||||
if (visit_directory &&
|
||||
!visit_directory(*child, error))
|
||||
return false;
|
||||
|
||||
if (recursive &&
|
||||
!child->Walk(recursive, filter,
|
||||
visit_directory, visit_song, visit_playlist,
|
||||
error))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "GlobalEvents.hxx"
|
||||
#include "util/Manual.hxx"
|
||||
#include "event/DeferredMonitor.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,12 +33,6 @@ class EventLoop;
|
||||
|
||||
namespace GlobalEvents {
|
||||
enum Event {
|
||||
/** database update was finished */
|
||||
UPDATE,
|
||||
|
||||
/** during database update, a song was deleted */
|
||||
DELETE,
|
||||
|
||||
/** an idle event was emitted */
|
||||
IDLE,
|
||||
|
||||
@@ -48,9 +42,6 @@ namespace GlobalEvents {
|
||||
/** the current song's tag has changed */
|
||||
TAG,
|
||||
|
||||
/** a hardware mixer plugin has detected a change */
|
||||
MIXER,
|
||||
|
||||
#ifdef WIN32
|
||||
/** shutdown requested */
|
||||
SHUTDOWN,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "thread/Thread.hxx"
|
||||
#include "thread/Name.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "util/Error.hxx"
|
||||
@@ -48,6 +49,8 @@ io_thread_run(void)
|
||||
static void
|
||||
io_thread_func(gcc_unused void *arg)
|
||||
{
|
||||
SetThreadName("io");
|
||||
|
||||
/* lock+unlock to synchronize with io_thread_start(), to be
|
||||
sure that io.thread is set */
|
||||
io.mutex.lock();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -49,7 +49,7 @@ io_thread_quit(void);
|
||||
void
|
||||
io_thread_deinit(void);
|
||||
|
||||
gcc_pure
|
||||
gcc_const
|
||||
EventLoop &
|
||||
io_thread_get();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,11 +20,10 @@
|
||||
#include "config.h"
|
||||
#include "IcyMetaDataParser.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/TagBuilder.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -37,7 +36,7 @@ IcyMetaDataParser::Reset()
|
||||
return;
|
||||
|
||||
if (data_rest == 0 && meta_size > 0)
|
||||
g_free(meta_data);
|
||||
delete[] meta_data;
|
||||
|
||||
delete tag;
|
||||
|
||||
@@ -66,7 +65,7 @@ IcyMetaDataParser::Data(size_t length)
|
||||
}
|
||||
|
||||
static void
|
||||
icy_add_item(Tag &tag, TagType type, const char *value)
|
||||
icy_add_item(TagBuilder &tag, TagType type, const char *value)
|
||||
{
|
||||
size_t length = strlen(value);
|
||||
|
||||
@@ -81,7 +80,7 @@ icy_add_item(Tag &tag, TagType type, const char *value)
|
||||
}
|
||||
|
||||
static void
|
||||
icy_parse_tag_item(Tag &tag, const char *name, const char *value)
|
||||
icy_parse_tag_item(TagBuilder &tag, const char *name, const char *value)
|
||||
{
|
||||
if (strcmp(name, "StreamTitle") == 0)
|
||||
icy_add_item(tag, TAG_TITLE, value);
|
||||
@@ -122,7 +121,7 @@ icy_parse_tag(char *p, char *const end)
|
||||
assert(end != nullptr);
|
||||
assert(p <= end);
|
||||
|
||||
Tag *tag = new Tag();
|
||||
TagBuilder tag;
|
||||
|
||||
while (p != end) {
|
||||
const char *const name = p;
|
||||
@@ -153,7 +152,7 @@ icy_parse_tag(char *p, char *const end)
|
||||
*quote = 0;
|
||||
p = quote + 1;
|
||||
|
||||
icy_parse_tag_item(*tag, name, value);
|
||||
icy_parse_tag_item(tag, name, value);
|
||||
|
||||
char *semicolon = std::find(p, end, ';');
|
||||
if (semicolon == end)
|
||||
@@ -161,7 +160,7 @@ icy_parse_tag(char *p, char *const end)
|
||||
p = semicolon + 1;
|
||||
}
|
||||
|
||||
return tag;
|
||||
return tag.CommitNew();
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -190,7 +189,7 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
|
||||
/* initialize metadata reader, allocate enough
|
||||
memory (+1 for the null terminator) */
|
||||
meta_position = 0;
|
||||
meta_data = (char *)g_malloc(meta_size + 1);
|
||||
meta_data = new char[meta_size + 1];
|
||||
}
|
||||
|
||||
assert(meta_position < meta_size);
|
||||
@@ -211,7 +210,7 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
|
||||
delete tag;
|
||||
|
||||
tag = icy_parse_tag(meta_data, meta_data + meta_size);
|
||||
g_free(meta_data);
|
||||
delete[] meta_data;
|
||||
|
||||
/* change back to normal data mode */
|
||||
|
||||
@@ -221,3 +220,32 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t
|
||||
IcyMetaDataParser::ParseInPlace(void *data, size_t length)
|
||||
{
|
||||
uint8_t *const dest0 = (uint8_t *)data;
|
||||
uint8_t *dest = dest0;
|
||||
const uint8_t *src = dest0;
|
||||
|
||||
while (length > 0) {
|
||||
size_t chunk = Data(length);
|
||||
if (chunk > 0) {
|
||||
memmove(dest, src, chunk);
|
||||
dest += chunk;
|
||||
src += chunk;
|
||||
length -= chunk;
|
||||
|
||||
if (length == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
chunk = Meta(src, length);
|
||||
if (chunk > 0) {
|
||||
src += chunk;
|
||||
length -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
return dest - dest0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -75,6 +75,13 @@ public:
|
||||
*/
|
||||
size_t Meta(const void *data, size_t length);
|
||||
|
||||
/**
|
||||
* Parse data and eliminate metadata.
|
||||
*
|
||||
* @return the number of data bytes remaining in the buffer
|
||||
*/
|
||||
size_t ParseInPlace(void *data, size_t length);
|
||||
|
||||
Tag *ReadTag() {
|
||||
Tag *result = tag;
|
||||
tag = nullptr;
|
||||
|
||||
20
src/Idle.cxx
20
src/Idle.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "config.h"
|
||||
#include "Idle.hxx"
|
||||
#include "GlobalEvents.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
@@ -44,6 +45,8 @@ static const char *const idle_names[] = {
|
||||
"update",
|
||||
"subscription",
|
||||
"message",
|
||||
"neighbor",
|
||||
"mount",
|
||||
nullptr
|
||||
};
|
||||
|
||||
@@ -69,3 +72,18 @@ idle_get_names(void)
|
||||
{
|
||||
return idle_names;
|
||||
}
|
||||
|
||||
unsigned
|
||||
idle_parse_name(const char *name)
|
||||
{
|
||||
#if !CLANG_CHECK_VERSION(3,6)
|
||||
/* disabled on clang due to -Wtautological-pointer-compare */
|
||||
assert(name != nullptr);
|
||||
#endif
|
||||
|
||||
for (unsigned i = 0; idle_names[i] != nullptr; ++i)
|
||||
if (StringEqualsCaseASCII(name, idle_names[i]))
|
||||
return 1 << i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
18
src/Idle.hxx
18
src/Idle.hxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,6 +25,8 @@
|
||||
#ifndef MPD_IDLE_HXX
|
||||
#define MPD_IDLE_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
/** song database has been updated*/
|
||||
static constexpr unsigned IDLE_DATABASE = 0x1;
|
||||
|
||||
@@ -59,6 +61,12 @@ static constexpr unsigned IDLE_SUBSCRIPTION = 0x200;
|
||||
/** a message on the subscribed channel was received */
|
||||
static constexpr unsigned IDLE_MESSAGE = 0x400;
|
||||
|
||||
/** a neighbor was found or lost */
|
||||
static constexpr unsigned IDLE_NEIGHBOR = 0x800;
|
||||
|
||||
/** the mount list has changed */
|
||||
static constexpr unsigned IDLE_MOUNT = 0x1000;
|
||||
|
||||
/**
|
||||
* Adds idle flag (with bitwise "or") and queues notifications to all
|
||||
* clients.
|
||||
@@ -78,4 +86,12 @@ idle_get(void);
|
||||
const char*const*
|
||||
idle_get_names(void);
|
||||
|
||||
/**
|
||||
* Parse an idle name and return its mask. Returns 0 if the given
|
||||
* name is unknown.
|
||||
*/
|
||||
gcc_nonnull_all gcc_pure
|
||||
unsigned
|
||||
idle_parse_name(const char *name);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "InputStream.hxx"
|
||||
#include "InputRegistry.hxx"
|
||||
#include "InputPlugin.hxx"
|
||||
#include "input/RewindInputPlugin.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h> /* for SEEK_SET */
|
||||
|
||||
static constexpr Domain input_domain("input");
|
||||
|
||||
InputStream *
|
||||
InputStream::Open(const char *url,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
{
|
||||
input_plugins_for_each_enabled(plugin) {
|
||||
InputStream *is;
|
||||
|
||||
is = plugin->open(url, mutex, cond, error);
|
||||
if (is != nullptr) {
|
||||
assert(is->plugin.close != nullptr);
|
||||
assert(is->plugin.read != nullptr);
|
||||
assert(is->plugin.eof != nullptr);
|
||||
assert(!is->seekable || is->plugin.seek != nullptr);
|
||||
|
||||
is = input_rewind_open(is);
|
||||
|
||||
return is;
|
||||
} else if (error.IsDefined())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
error.Set(input_domain, "Unrecognized URI");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::Check(Error &error)
|
||||
{
|
||||
return plugin.check == nullptr || plugin.check(this, error);
|
||||
}
|
||||
|
||||
void
|
||||
InputStream::Update()
|
||||
{
|
||||
if (plugin.update != nullptr)
|
||||
plugin.update(this);
|
||||
}
|
||||
|
||||
void
|
||||
InputStream::WaitReady()
|
||||
{
|
||||
while (true) {
|
||||
Update();
|
||||
if (ready)
|
||||
break;
|
||||
|
||||
cond.wait(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputStream::LockWaitReady()
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
WaitReady();
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::CheapSeeking() const
|
||||
{
|
||||
return IsSeekable() && !uri_has_scheme(uri.c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::Seek(offset_type _offset, int whence, Error &error)
|
||||
{
|
||||
if (plugin.seek == nullptr)
|
||||
return false;
|
||||
|
||||
return plugin.seek(this, _offset, whence, error);
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::LockSeek(offset_type _offset, int whence, Error &error)
|
||||
{
|
||||
if (plugin.seek == nullptr)
|
||||
return false;
|
||||
|
||||
const ScopeLock protect(mutex);
|
||||
return Seek(_offset, whence, error);
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::Rewind(Error &error)
|
||||
{
|
||||
return Seek(0, SEEK_SET, error);
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::LockRewind(Error &error)
|
||||
{
|
||||
return LockSeek(0, SEEK_SET, error);
|
||||
}
|
||||
|
||||
Tag *
|
||||
InputStream::ReadTag()
|
||||
{
|
||||
return plugin.tag != nullptr
|
||||
? plugin.tag(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
Tag *
|
||||
InputStream::LockReadTag()
|
||||
{
|
||||
if (plugin.tag == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const ScopeLock protect(mutex);
|
||||
return ReadTag();
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::IsAvailable()
|
||||
{
|
||||
return plugin.available != nullptr
|
||||
? plugin.available(this)
|
||||
: true;
|
||||
}
|
||||
|
||||
size_t
|
||||
InputStream::Read(void *ptr, size_t _size, Error &error)
|
||||
{
|
||||
assert(ptr != nullptr);
|
||||
assert(_size > 0);
|
||||
|
||||
return plugin.read(this, ptr, _size, error);
|
||||
}
|
||||
|
||||
size_t
|
||||
InputStream::LockRead(void *ptr, size_t _size, Error &error)
|
||||
{
|
||||
assert(ptr != nullptr);
|
||||
assert(_size > 0);
|
||||
|
||||
const ScopeLock protect(mutex);
|
||||
return Read(ptr, _size, error);
|
||||
}
|
||||
|
||||
void
|
||||
InputStream::Close()
|
||||
{
|
||||
plugin.close(this);
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::IsEOF()
|
||||
{
|
||||
return plugin.eof(this);
|
||||
}
|
||||
|
||||
bool
|
||||
InputStream::LockIsEOF()
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
return IsEOF();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,19 +21,26 @@
|
||||
#include "Instance.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Idle.hxx"
|
||||
#include "Stats.hxx"
|
||||
|
||||
void
|
||||
Instance::DeleteSong(const Song &song)
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/DatabaseError.hxx"
|
||||
#include "db/LightSong.hxx"
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
#include "sticker/StickerDatabase.hxx"
|
||||
#include "sticker/SongSticker.hxx"
|
||||
#endif
|
||||
|
||||
Database *
|
||||
Instance::GetDatabase(Error &error)
|
||||
{
|
||||
partition->DeleteSong(song);
|
||||
if (database == nullptr)
|
||||
error.Set(db_domain, DB_DISABLED, "No database");
|
||||
return database;
|
||||
}
|
||||
|
||||
void
|
||||
Instance::DatabaseModified()
|
||||
{
|
||||
partition->DatabaseModified();
|
||||
idle_add(IDLE_DATABASE);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
Instance::TagModified()
|
||||
@@ -46,3 +53,50 @@ Instance::SyncWithPlayer()
|
||||
{
|
||||
partition->SyncWithPlayer();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
void
|
||||
Instance::OnDatabaseModified()
|
||||
{
|
||||
assert(database != nullptr);
|
||||
|
||||
/* propagate the change to all subsystems */
|
||||
|
||||
stats_invalidate();
|
||||
partition->DatabaseModified(*database);
|
||||
idle_add(IDLE_DATABASE);
|
||||
}
|
||||
|
||||
void
|
||||
Instance::OnDatabaseSongRemoved(const LightSong &song)
|
||||
{
|
||||
assert(database != nullptr);
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
/* if the song has a sticker, remove it */
|
||||
if (sticker_enabled())
|
||||
sticker_song_delete(song);
|
||||
#endif
|
||||
|
||||
const auto uri = song.GetURI();
|
||||
partition->DeleteSong(uri.c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
|
||||
void
|
||||
Instance::FoundNeighbor(gcc_unused const NeighborInfo &info)
|
||||
{
|
||||
idle_add(IDLE_NEIGHBOR);
|
||||
}
|
||||
|
||||
void
|
||||
Instance::LostNeighbor(gcc_unused const NeighborInfo &info)
|
||||
{
|
||||
idle_add(IDLE_NEIGHBOR);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -21,23 +21,76 @@
|
||||
#define MPD_INSTANCE_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
#include "neighbor/Listener.hxx"
|
||||
class NeighborGlue;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/DatabaseListener.hxx"
|
||||
class Database;
|
||||
class Storage;
|
||||
class UpdateService;
|
||||
#endif
|
||||
|
||||
class EventLoop;
|
||||
class Error;
|
||||
class ClientList;
|
||||
struct Partition;
|
||||
struct Song;
|
||||
|
||||
struct Instance {
|
||||
struct Instance final
|
||||
#if defined(ENABLE_DATABASE) || defined(ENABLE_NEIGHBOR_PLUGINS)
|
||||
:
|
||||
#endif
|
||||
#ifdef ENABLE_DATABASE
|
||||
public DatabaseListener
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
,
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
public NeighborListener
|
||||
#endif
|
||||
{
|
||||
EventLoop *event_loop;
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
NeighborGlue *neighbors;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
Database *database;
|
||||
|
||||
/**
|
||||
* This is really a #CompositeStorage. To avoid heavy include
|
||||
* dependencies, we declare it as just #Storage.
|
||||
*/
|
||||
Storage *storage;
|
||||
|
||||
UpdateService *update;
|
||||
#endif
|
||||
|
||||
ClientList *client_list;
|
||||
|
||||
Partition *partition;
|
||||
|
||||
void DeleteSong(const Song &song);
|
||||
Instance() {
|
||||
#ifdef ENABLE_DATABASE
|
||||
storage = nullptr;
|
||||
update = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
/**
|
||||
* The database has been modified. Propagate the change to
|
||||
* all subsystems.
|
||||
* Returns the global #Database instance. May return nullptr
|
||||
* if this MPD configuration has no database (no
|
||||
* music_directory was configured).
|
||||
*/
|
||||
void DatabaseModified();
|
||||
Database *GetDatabase(Error &error);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A tag in the play queue has been modified by the player
|
||||
@@ -49,6 +102,18 @@ struct Instance {
|
||||
* Synchronize the player with the play queue.
|
||||
*/
|
||||
void SyncWithPlayer();
|
||||
|
||||
private:
|
||||
#ifdef ENABLE_DATABASE
|
||||
virtual void OnDatabaseModified() override;
|
||||
virtual void OnDatabaseSongRemoved(const LightSong &song) override;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
/* virtual methods from class NeighborListener */
|
||||
virtual void FoundNeighbor(const NeighborInfo &info) override;
|
||||
virtual void LostNeighbor(const NeighborInfo &info) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,12 +19,10 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "Listen.hxx"
|
||||
#include "Main.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "Client.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "config/ConfigData.hxx"
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "config/ConfigOption.hxx"
|
||||
#include "event/ServerSocket.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
@@ -43,13 +41,16 @@ static constexpr Domain listen_domain("listen");
|
||||
#define DEFAULT_PORT 6600
|
||||
|
||||
class ClientListener final : public ServerSocket {
|
||||
Partition &partition;
|
||||
|
||||
public:
|
||||
ClientListener():ServerSocket(*main_loop) {}
|
||||
ClientListener(EventLoop &_loop, Partition &_partition)
|
||||
:ServerSocket(_loop), partition(_partition) {}
|
||||
|
||||
private:
|
||||
virtual void OnAccept(int fd, const sockaddr &address,
|
||||
size_t address_length, int uid) {
|
||||
client_new(*main_loop, *instance->partition,
|
||||
client_new(GetEventLoop(), partition,
|
||||
fd, &address, address_length, uid);
|
||||
}
|
||||
};
|
||||
@@ -76,10 +77,11 @@ listen_add_config_param(unsigned int port,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
|
||||
static bool
|
||||
listen_systemd_activation(Error &error_r)
|
||||
{
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
int n = sd_listen_fds(true);
|
||||
if (n <= 0) {
|
||||
if (n < 0)
|
||||
@@ -94,29 +96,26 @@ listen_systemd_activation(Error &error_r)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void)error_r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
listen_global_init(Error &error)
|
||||
{
|
||||
assert(main_loop != nullptr);
|
||||
#endif
|
||||
|
||||
bool
|
||||
listen_global_init(EventLoop &loop, Partition &partition, Error &error)
|
||||
{
|
||||
int port = config_get_positive(CONF_PORT, DEFAULT_PORT);
|
||||
const struct config_param *param =
|
||||
config_get_next_param(CONF_BIND_TO_ADDRESS, nullptr);
|
||||
bool success;
|
||||
config_get_param(CONF_BIND_TO_ADDRESS);
|
||||
|
||||
listen_socket = new ClientListener();
|
||||
listen_socket = new ClientListener(loop, partition);
|
||||
|
||||
#ifdef ENABLE_SYSTEMD_DAEMON
|
||||
if (listen_systemd_activation(error))
|
||||
return true;
|
||||
|
||||
if (error.IsDefined())
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (param != nullptr) {
|
||||
/* "bind_to_address" is configured, create listeners
|
||||
@@ -130,16 +129,12 @@ listen_global_init(Error &error)
|
||||
param->line);
|
||||
return false;
|
||||
}
|
||||
|
||||
param = config_get_next_param(CONF_BIND_TO_ADDRESS,
|
||||
param);
|
||||
} while (param != nullptr);
|
||||
} while ((param = param->next) != nullptr);
|
||||
} else {
|
||||
/* no "bind_to_address" configured, bind the
|
||||
configured port on all interfaces */
|
||||
|
||||
success = listen_socket->AddPort(port, error);
|
||||
if (!success) {
|
||||
if (!listen_socket->AddPort(port, error)) {
|
||||
delete listen_socket;
|
||||
error.FormatPrefix("Failed to listen on *:%d: ", port);
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,12 +20,14 @@
|
||||
#ifndef MPD_LISTEN_HXX
|
||||
#define MPD_LISTEN_HXX
|
||||
|
||||
class EventLoop;
|
||||
class Error;
|
||||
struct Partition;
|
||||
|
||||
extern int listen_port;
|
||||
|
||||
bool
|
||||
listen_global_init(Error &error);
|
||||
listen_global_init(EventLoop &loop, Partition &partition, Error &error);
|
||||
|
||||
void listen_global_finish(void);
|
||||
|
||||
|
||||
55
src/Log.cxx
55
src/Log.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,62 +19,19 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "LogV.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "system/fd_util.h"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
static GLogLevelFlags
|
||||
ToGLib(LogLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
case LogLevel::DEBUG:
|
||||
return G_LOG_LEVEL_DEBUG;
|
||||
|
||||
case LogLevel::INFO:
|
||||
return G_LOG_LEVEL_INFO;
|
||||
|
||||
case LogLevel::DEFAULT:
|
||||
return G_LOG_LEVEL_MESSAGE;
|
||||
|
||||
case LogLevel::WARNING:
|
||||
case LogLevel::ERROR:
|
||||
return G_LOG_LEVEL_WARNING;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
void
|
||||
Log(const Domain &domain, LogLevel level, const char *msg)
|
||||
{
|
||||
g_log(domain.GetName(), ToGLib(level), "%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
LogFormatV(const Domain &domain, LogLevel level, const char *fmt, va_list ap)
|
||||
{
|
||||
g_logv(domain.GetName(), ToGLib(level), fmt, ap);
|
||||
char msg[1024];
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
Log(domain, level, msg);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -159,7 +116,7 @@ FormatError(const Error &error, const char *fmt, ...)
|
||||
void
|
||||
LogErrno(const Domain &domain, int e, const char *msg)
|
||||
{
|
||||
LogFormat(domain, LogLevel::ERROR, "%s: %s", msg, g_strerror(e));
|
||||
LogFormat(domain, LogLevel::ERROR, "%s: %s", msg, strerror(e));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
39
src/Log.hxx
39
src/Log.hxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,47 +20,12 @@
|
||||
#ifndef MPD_LOG_HXX
|
||||
#define MPD_LOG_HXX
|
||||
|
||||
#include "LogLevel.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
/* damn you, windows.h! */
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class Error;
|
||||
class Domain;
|
||||
|
||||
enum class LogLevel {
|
||||
/**
|
||||
* Debug message for developers.
|
||||
*/
|
||||
DEBUG,
|
||||
|
||||
/**
|
||||
* Unimportant informational message.
|
||||
*/
|
||||
INFO,
|
||||
|
||||
/**
|
||||
* Interesting informational message.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* Warning: something may be wrong.
|
||||
*/
|
||||
WARNING,
|
||||
|
||||
/**
|
||||
* An error has occurred, an operation could not finish
|
||||
* successfully.
|
||||
*/
|
||||
ERROR,
|
||||
};
|
||||
|
||||
void
|
||||
Log(const Domain &domain, LogLevel level, const char *msg);
|
||||
|
||||
|
||||
230
src/LogBackend.cxx
Normal file
230
src/LogBackend.cxx
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "LogBackend.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
|
||||
static int
|
||||
ToAndroidLogLevel(LogLevel log_level)
|
||||
{
|
||||
switch (log_level) {
|
||||
case LogLevel::DEBUG:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
|
||||
case LogLevel::INFO:
|
||||
case LogLevel::DEFAULT:
|
||||
return ANDROID_LOG_INFO;
|
||||
|
||||
case LogLevel::WARNING:
|
||||
return ANDROID_LOG_WARN;
|
||||
|
||||
case LogLevel::ERROR:
|
||||
return ANDROID_LOG_ERROR;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static LogLevel log_threshold = LogLevel::INFO;
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
static const char *log_charset;
|
||||
#endif
|
||||
|
||||
static bool enable_timestamp;
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
static bool enable_syslog;
|
||||
#endif
|
||||
|
||||
void
|
||||
SetLogThreshold(LogLevel _threshold)
|
||||
{
|
||||
log_threshold = _threshold;
|
||||
}
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
|
||||
void
|
||||
SetLogCharset(const char *_charset)
|
||||
{
|
||||
log_charset = _charset;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
EnableLogTimestamp()
|
||||
{
|
||||
#ifdef HAVE_SYSLOG
|
||||
assert(!enable_syslog);
|
||||
#endif
|
||||
assert(!enable_timestamp);
|
||||
|
||||
enable_timestamp = true;
|
||||
}
|
||||
|
||||
static const char *log_date(void)
|
||||
{
|
||||
static constexpr size_t LOG_DATE_BUF_SIZE = 16;
|
||||
static char buf[LOG_DATE_BUF_SIZE];
|
||||
time_t t = time(nullptr);
|
||||
strftime(buf, LOG_DATE_BUF_SIZE, "%b %d %H:%M : ", localtime(&t));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the length of the string excluding trailing whitespace
|
||||
* characters.
|
||||
*/
|
||||
static int
|
||||
chomp_length(const char *p)
|
||||
{
|
||||
size_t length = strlen(p);
|
||||
return StripRight(p, length);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
|
||||
static int
|
||||
ToSysLogLevel(LogLevel log_level)
|
||||
{
|
||||
switch (log_level) {
|
||||
case LogLevel::DEBUG:
|
||||
return LOG_DEBUG;
|
||||
|
||||
case LogLevel::INFO:
|
||||
return LOG_INFO;
|
||||
|
||||
case LogLevel::DEFAULT:
|
||||
return LOG_NOTICE;
|
||||
|
||||
case LogLevel::WARNING:
|
||||
return LOG_WARNING;
|
||||
|
||||
case LogLevel::ERROR:
|
||||
return LOG_ERR;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
static void
|
||||
SysLog(const Domain &domain, LogLevel log_level, const char *message)
|
||||
{
|
||||
syslog(ToSysLogLevel(log_level), "%s: %.*s",
|
||||
domain.GetName(),
|
||||
chomp_length(message), message);
|
||||
}
|
||||
|
||||
void
|
||||
LogInitSysLog()
|
||||
{
|
||||
openlog(PACKAGE, 0, LOG_DAEMON);
|
||||
enable_syslog = true;
|
||||
}
|
||||
|
||||
void
|
||||
LogFinishSysLog()
|
||||
{
|
||||
if (enable_syslog)
|
||||
closelog();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
FileLog(const Domain &domain, const char *message)
|
||||
{
|
||||
#ifdef HAVE_GLIB
|
||||
char *converted;
|
||||
|
||||
if (log_charset != nullptr) {
|
||||
converted = g_convert_with_fallback(message, -1,
|
||||
log_charset, "utf-8",
|
||||
nullptr, nullptr,
|
||||
nullptr, nullptr);
|
||||
if (converted != nullptr)
|
||||
message = converted;
|
||||
} else
|
||||
converted = nullptr;
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "%s%s: %.*s\n",
|
||||
enable_timestamp ? log_date() : "",
|
||||
domain.GetName(),
|
||||
chomp_length(message), message);
|
||||
|
||||
#ifdef WIN32
|
||||
/* force-flush the log file, because setvbuf() does not seem
|
||||
to have an effect on WIN32 */
|
||||
fflush(stderr);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
g_free(converted);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* !ANDROID */
|
||||
|
||||
void
|
||||
Log(const Domain &domain, LogLevel level, const char *msg)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ToAndroidLogLevel(level), "MPD",
|
||||
"%s: %s", domain.GetName(), msg);
|
||||
#else
|
||||
|
||||
if (level < log_threshold)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
if (enable_syslog) {
|
||||
SysLog(domain, level, msg);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
FileLog(domain, msg);
|
||||
#endif /* !ANDROID */
|
||||
}
|
||||
45
src/LogBackend.hxx
Normal file
45
src/LogBackend.hxx
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_LOG_BACKEND_HXX
|
||||
#define MPD_LOG_BACKEND_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "LogLevel.hxx"
|
||||
|
||||
void
|
||||
SetLogThreshold(LogLevel _threshold);
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
|
||||
void
|
||||
SetLogCharset(const char *_charset);
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
EnableLogTimestamp();
|
||||
|
||||
void
|
||||
LogInitSysLog();
|
||||
|
||||
void
|
||||
LogFinishSysLog();
|
||||
|
||||
#endif /* LOG_H */
|
||||
277
src/LogInit.cxx
277
src/LogInit.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,48 +19,38 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "LogInit.hxx"
|
||||
#include "LogBackend.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "system/fd_util.h"
|
||||
#include "config/ConfigData.hxx"
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "config/ConfigOption.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/CharUtil.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_GLIB
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#define LOG_LEVEL_SECURE G_LOG_LEVEL_INFO
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LOG_LEVEL_SECURE LogLevel::INFO
|
||||
|
||||
#define LOG_DATE_BUF_SIZE 16
|
||||
#define LOG_DATE_LEN (LOG_DATE_BUF_SIZE - 1)
|
||||
|
||||
static constexpr Domain log_domain("log");
|
||||
|
||||
static GLogLevelFlags log_threshold = G_LOG_LEVEL_MESSAGE;
|
||||
#ifndef ANDROID
|
||||
|
||||
static const char *log_charset;
|
||||
|
||||
static bool stdout_mode = true;
|
||||
static int out_fd;
|
||||
static AllocatedPath out_path = AllocatedPath::Null();
|
||||
|
||||
@@ -73,66 +63,6 @@ static void redirect_logs(int fd)
|
||||
FatalSystemError("Failed to dup2 stderr");
|
||||
}
|
||||
|
||||
static const char *log_date(void)
|
||||
{
|
||||
static char buf[LOG_DATE_BUF_SIZE];
|
||||
time_t t = time(nullptr);
|
||||
strftime(buf, LOG_DATE_BUF_SIZE, "%b %d %H:%M : ", localtime(&t));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the length of the string excluding trailing whitespace
|
||||
* characters.
|
||||
*/
|
||||
static int
|
||||
chomp_length(const char *p)
|
||||
{
|
||||
size_t length = strlen(p);
|
||||
|
||||
while (length > 0 && IsWhitespaceOrNull(p[length - 1]))
|
||||
--length;
|
||||
|
||||
return (int)length;
|
||||
}
|
||||
|
||||
static void
|
||||
file_log_func(const gchar *domain,
|
||||
GLogLevelFlags log_level,
|
||||
const gchar *message, gcc_unused gpointer user_data)
|
||||
{
|
||||
char *converted;
|
||||
|
||||
if (log_level > log_threshold)
|
||||
return;
|
||||
|
||||
if (log_charset != nullptr) {
|
||||
converted = g_convert_with_fallback(message, -1,
|
||||
log_charset, "utf-8",
|
||||
nullptr, nullptr,
|
||||
nullptr, nullptr);
|
||||
if (converted != nullptr)
|
||||
message = converted;
|
||||
} else
|
||||
converted = nullptr;
|
||||
|
||||
if (domain == nullptr)
|
||||
domain = "";
|
||||
|
||||
fprintf(stderr, "%s%s%s%.*s\n",
|
||||
stdout_mode ? "" : log_date(),
|
||||
domain, *domain == 0 ? "" : ": ",
|
||||
chomp_length(message), message);
|
||||
|
||||
g_free(converted);
|
||||
}
|
||||
|
||||
static void
|
||||
log_init_stdout(void)
|
||||
{
|
||||
g_log_set_default_handler(file_log_func, nullptr);
|
||||
}
|
||||
|
||||
static int
|
||||
open_log_file(void)
|
||||
{
|
||||
@@ -154,112 +84,66 @@ log_init_file(unsigned line, Error &error)
|
||||
return false;
|
||||
}
|
||||
|
||||
g_log_set_default_handler(file_log_func, nullptr);
|
||||
EnableLogTimestamp();
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
|
||||
static int
|
||||
glib_to_syslog_level(GLogLevelFlags log_level)
|
||||
static inline LogLevel
|
||||
parse_log_level(const char *value, unsigned line)
|
||||
{
|
||||
switch (log_level & G_LOG_LEVEL_MASK) {
|
||||
case G_LOG_LEVEL_ERROR:
|
||||
case G_LOG_LEVEL_CRITICAL:
|
||||
return LOG_ERR;
|
||||
|
||||
case G_LOG_LEVEL_WARNING:
|
||||
return LOG_WARNING;
|
||||
|
||||
case G_LOG_LEVEL_MESSAGE:
|
||||
return LOG_NOTICE;
|
||||
|
||||
case G_LOG_LEVEL_INFO:
|
||||
return LOG_INFO;
|
||||
|
||||
case G_LOG_LEVEL_DEBUG:
|
||||
return LOG_DEBUG;
|
||||
|
||||
default:
|
||||
return LOG_NOTICE;
|
||||
if (0 == strcmp(value, "default"))
|
||||
return LogLevel::DEFAULT;
|
||||
if (0 == strcmp(value, "secure"))
|
||||
return LOG_LEVEL_SECURE;
|
||||
else if (0 == strcmp(value, "verbose"))
|
||||
return LogLevel::DEBUG;
|
||||
else {
|
||||
FormatFatalError("unknown log level \"%s\" at line %u",
|
||||
value, line);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
syslog_log_func(const gchar *domain,
|
||||
GLogLevelFlags log_level, const gchar *message,
|
||||
gcc_unused gpointer user_data)
|
||||
{
|
||||
if (stdout_mode) {
|
||||
/* fall back to the file log function during
|
||||
startup */
|
||||
file_log_func(domain, log_level,
|
||||
message, user_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_level > log_threshold)
|
||||
return;
|
||||
|
||||
if (domain == nullptr)
|
||||
domain = "";
|
||||
|
||||
syslog(glib_to_syslog_level(log_level), "%s%s%.*s",
|
||||
domain, *domain == 0 ? "" : ": ",
|
||||
chomp_length(message), message);
|
||||
}
|
||||
|
||||
static void
|
||||
log_init_syslog(void)
|
||||
{
|
||||
assert(out_path.IsNull());
|
||||
|
||||
openlog(PACKAGE, 0, LOG_DAEMON);
|
||||
g_log_set_default_handler(syslog_log_func, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline GLogLevelFlags
|
||||
parse_log_level(const char *value, unsigned line)
|
||||
{
|
||||
if (0 == strcmp(value, "default"))
|
||||
return G_LOG_LEVEL_MESSAGE;
|
||||
if (0 == strcmp(value, "secure"))
|
||||
return LOG_LEVEL_SECURE;
|
||||
else if (0 == strcmp(value, "verbose"))
|
||||
return G_LOG_LEVEL_DEBUG;
|
||||
else {
|
||||
FormatFatalError("unknown log level \"%s\" at line %u",
|
||||
value, line);
|
||||
return G_LOG_LEVEL_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
log_early_init(bool verbose)
|
||||
{
|
||||
if (verbose)
|
||||
log_threshold = G_LOG_LEVEL_DEBUG;
|
||||
#ifdef ANDROID
|
||||
(void)verbose;
|
||||
#else
|
||||
/* force stderr to be line-buffered */
|
||||
setvbuf(stderr, nullptr, _IOLBF, 0);
|
||||
|
||||
log_init_stdout();
|
||||
if (verbose)
|
||||
SetLogThreshold(LogLevel::DEBUG);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
log_init(bool verbose, bool use_stdout, Error &error)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
(void)verbose;
|
||||
(void)use_stdout;
|
||||
(void)error;
|
||||
|
||||
return true;
|
||||
#else
|
||||
const struct config_param *param;
|
||||
|
||||
g_get_charset(&log_charset);
|
||||
#ifdef HAVE_GLIB
|
||||
const char *charset;
|
||||
g_get_charset(&charset);
|
||||
SetLogCharset(charset);
|
||||
#endif
|
||||
|
||||
if (verbose)
|
||||
log_threshold = G_LOG_LEVEL_DEBUG;
|
||||
SetLogThreshold(LogLevel::DEBUG);
|
||||
else if ((param = config_get_param(CONF_LOG_LEVEL)) != nullptr)
|
||||
log_threshold = parse_log_level(param->value.c_str(),
|
||||
param->line);
|
||||
SetLogThreshold(parse_log_level(param->value.c_str(),
|
||||
param->line));
|
||||
|
||||
if (use_stdout) {
|
||||
log_init_stdout();
|
||||
return true;
|
||||
} else {
|
||||
param = config_get_param(CONF_LOG_FILE);
|
||||
@@ -267,7 +151,7 @@ log_init(bool verbose, bool use_stdout, Error &error)
|
||||
#ifdef HAVE_SYSLOG
|
||||
/* no configuration: default to syslog (if
|
||||
available) */
|
||||
log_init_syslog();
|
||||
LogInitSysLog();
|
||||
return true;
|
||||
#else
|
||||
error.Set(log_domain,
|
||||
@@ -276,7 +160,7 @@ log_init(bool verbose, bool use_stdout, Error &error)
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG
|
||||
} else if (strcmp(param->value.c_str(), "syslog") == 0) {
|
||||
log_init_syslog();
|
||||
LogInitSysLog();
|
||||
return true;
|
||||
#endif
|
||||
} else {
|
||||
@@ -285,56 +169,70 @@ log_init(bool verbose, bool use_stdout, Error &error)
|
||||
log_init_file(param->line, error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
static void
|
||||
close_log_files(void)
|
||||
{
|
||||
if (stdout_mode)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_SYSLOG
|
||||
if (out_path.IsNull())
|
||||
closelog();
|
||||
LogFinishSysLog();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
log_deinit(void)
|
||||
{
|
||||
#ifndef ANDROID
|
||||
close_log_files();
|
||||
out_path = AllocatedPath::Null();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void setup_log_output(bool use_stdout)
|
||||
{
|
||||
fflush(nullptr);
|
||||
if (!use_stdout) {
|
||||
#ifndef WIN32
|
||||
if (out_path.IsNull())
|
||||
out_fd = open("/dev/null", O_WRONLY);
|
||||
#endif
|
||||
#ifdef ANDROID
|
||||
(void)use_stdout;
|
||||
#else
|
||||
if (use_stdout)
|
||||
return;
|
||||
|
||||
fflush(nullptr);
|
||||
|
||||
if (out_fd < 0) {
|
||||
#ifdef WIN32
|
||||
return;
|
||||
#else
|
||||
out_fd = open("/dev/null", O_WRONLY);
|
||||
if (out_fd < 0)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (out_fd >= 0) {
|
||||
redirect_logs(out_fd);
|
||||
close(out_fd);
|
||||
}
|
||||
out_fd = -1;
|
||||
|
||||
stdout_mode = false;
|
||||
log_charset = nullptr;
|
||||
}
|
||||
#ifdef HAVE_GLIB
|
||||
SetLogCharset(nullptr);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
int cycle_log_files(void)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
return 0;
|
||||
#else
|
||||
int fd;
|
||||
|
||||
if (stdout_mode || out_path.IsNull())
|
||||
if (out_path.IsNull())
|
||||
return 0;
|
||||
|
||||
assert(!out_path.IsNull());
|
||||
|
||||
FormatDebug(log_domain, "Cycling log files");
|
||||
close_log_files();
|
||||
|
||||
@@ -348,6 +246,9 @@ int cycle_log_files(void)
|
||||
}
|
||||
|
||||
redirect_logs(fd);
|
||||
close(fd);
|
||||
|
||||
FormatDebug(log_domain, "Done cycling log files");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
59
src/LogLevel.hxx
Normal file
59
src/LogLevel.hxx
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_LOG_LEVEL_HXX
|
||||
#define MPD_LOG_LEVEL_HXX
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
/* damn you, windows.h! */
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
enum class LogLevel {
|
||||
/**
|
||||
* Debug message for developers.
|
||||
*/
|
||||
DEBUG,
|
||||
|
||||
/**
|
||||
* Unimportant informational message.
|
||||
*/
|
||||
INFO,
|
||||
|
||||
/**
|
||||
* Interesting informational message.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* Warning: something may be wrong.
|
||||
*/
|
||||
WARNING,
|
||||
|
||||
/**
|
||||
* An error has occurred, an operation could not finish
|
||||
* successfully.
|
||||
*/
|
||||
ERROR,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_LOGV_HXX
|
||||
#define MPD_LOGV_HXX
|
||||
|
||||
#include "Log.hxx"
|
||||
#include "Log.hxx" // IWYU pragma: export
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
464
src/Main.cxx
464
src/Main.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,67 +23,86 @@
|
||||
#include "CommandLine.hxx"
|
||||
#include "PlaylistFile.hxx"
|
||||
#include "PlaylistGlobal.hxx"
|
||||
#include "UpdateGlue.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "StateFile.hxx"
|
||||
#include "PlayerThread.hxx"
|
||||
#include "Mapper.hxx"
|
||||
#include "DatabaseGlue.hxx"
|
||||
#include "DatabaseSimple.hxx"
|
||||
#include "Permission.hxx"
|
||||
#include "Listen.hxx"
|
||||
#include "Client.hxx"
|
||||
#include "ClientList.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "client/ClientList.hxx"
|
||||
#include "command/AllCommands.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Volume.hxx"
|
||||
#include "OutputAll.hxx"
|
||||
#include "tag/TagConfig.hxx"
|
||||
#include "ReplayGainConfig.hxx"
|
||||
#include "Idle.hxx"
|
||||
#include "SignalHandlers.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "LogInit.hxx"
|
||||
#include "GlobalEvents.hxx"
|
||||
#include "InputInit.hxx"
|
||||
#include "input/Init.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
#include "IOThread.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Config.hxx"
|
||||
#include "PlaylistRegistry.hxx"
|
||||
#include "ZeroconfGlue.hxx"
|
||||
#include "DecoderList.hxx"
|
||||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "zeroconf/ZeroconfGlue.hxx"
|
||||
#include "decoder/DecoderList.hxx"
|
||||
#include "AudioConfig.hxx"
|
||||
#include "pcm/PcmResample.hxx"
|
||||
#include "Daemon.hxx"
|
||||
#include "pcm/PcmConvert.hxx"
|
||||
#include "unix/SignalHandlers.hxx"
|
||||
#include "unix/Daemon.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "thread/Id.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigDefaults.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "thread/Slack.hxx"
|
||||
#include "lib/icu/Init.hxx"
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "config/ConfigData.hxx"
|
||||
#include "config/ConfigDefaults.hxx"
|
||||
#include "config/ConfigOption.hxx"
|
||||
#include "config/ConfigError.hxx"
|
||||
#include "Stats.hxx"
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "db/update/Service.hxx"
|
||||
#include "db/Configured.hxx"
|
||||
#include "db/DatabasePlugin.hxx"
|
||||
#include "db/plugins/simple/SimpleDatabasePlugin.hxx"
|
||||
#include "storage/Configured.hxx"
|
||||
#include "storage/CompositeStorage.hxx"
|
||||
#ifdef ENABLE_INOTIFY
|
||||
#include "InotifyUpdate.hxx"
|
||||
#include "db/update/InotifyUpdate.hxx"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
#include "neighbor/Glue.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
#include "StickerDatabase.hxx"
|
||||
#include "sticker/StickerDatabase.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
#include "ArchiveList.hxx"
|
||||
#include "archive/ArchiveList.hxx"
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#ifdef ANDROID
|
||||
#include "java/Global.hxx"
|
||||
#include "java/File.hxx"
|
||||
#include "android/Environment.hxx"
|
||||
#include "android/Context.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "org_musicpd_Bridge.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
#include <glib.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_LOCALE_H
|
||||
#include <locale.h>
|
||||
@@ -94,18 +113,25 @@
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096;
|
||||
static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;
|
||||
|
||||
static constexpr Domain main_domain("main");
|
||||
|
||||
ThreadId main_thread;
|
||||
EventLoop *main_loop;
|
||||
#ifdef ANDROID
|
||||
Context *context;
|
||||
#endif
|
||||
|
||||
Instance *instance;
|
||||
|
||||
static StateFile *state_file;
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
static bool
|
||||
glue_daemonize_init(const struct options *options, Error &error)
|
||||
{
|
||||
@@ -123,28 +149,33 @@ glue_daemonize_init(const struct options *options, Error &error)
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool
|
||||
glue_mapper_init(Error &error)
|
||||
{
|
||||
auto music_dir = config_get_path(CONF_MUSIC_DIR, error);
|
||||
if (music_dir.IsNull() && error.IsDefined())
|
||||
return false;
|
||||
|
||||
auto playlist_dir = config_get_path(CONF_PLAYLIST_DIR, error);
|
||||
if (playlist_dir.IsNull() && error.IsDefined())
|
||||
return false;
|
||||
|
||||
if (music_dir.IsNull()) {
|
||||
const char *path =
|
||||
g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
|
||||
if (path != nullptr) {
|
||||
music_dir = AllocatedPath::FromUTF8(path, error);
|
||||
if (music_dir.IsNull())
|
||||
return false;
|
||||
}
|
||||
mapper_init(std::move(playlist_dir));
|
||||
return true;
|
||||
}
|
||||
|
||||
mapper_init(std::move(music_dir), std::move(playlist_dir));
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
static bool
|
||||
InitStorage(Error &error)
|
||||
{
|
||||
Storage *storage = CreateConfiguredStorage(io_thread_get(), error);
|
||||
if (storage == nullptr)
|
||||
return !error.IsDefined();
|
||||
|
||||
assert(!error.IsDefined());
|
||||
|
||||
CompositeStorage *composite = new CompositeStorage();
|
||||
instance->storage = composite;
|
||||
composite->Mount("", storage);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -156,50 +187,60 @@ glue_mapper_init(Error &error)
|
||||
static bool
|
||||
glue_db_init_and_load(void)
|
||||
{
|
||||
const struct config_param *param = config_get_param(CONF_DATABASE);
|
||||
const struct config_param *path = config_get_param(CONF_DB_FILE);
|
||||
Error error;
|
||||
instance->database =
|
||||
CreateConfiguredDatabase(*instance->event_loop, *instance,
|
||||
error);
|
||||
if (instance->database == nullptr) {
|
||||
if (error.IsDefined())
|
||||
FatalError(error);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
if (param != nullptr && path != nullptr)
|
||||
LogWarning(main_domain,
|
||||
"Found both 'database' and 'db_file' setting - ignoring the latter");
|
||||
if (instance->database->GetPlugin().flags & DatabasePlugin::FLAG_REQUIRE_STORAGE) {
|
||||
if (!InitStorage(error))
|
||||
FatalError(error);
|
||||
|
||||
if (!mapper_has_music_directory()) {
|
||||
if (param != nullptr)
|
||||
LogDefault(main_domain,
|
||||
if (instance->storage == nullptr) {
|
||||
delete instance->database;
|
||||
instance->database = nullptr;
|
||||
LogDefault(config_domain,
|
||||
"Found database setting without "
|
||||
"music_directory - disabling database");
|
||||
if (path != nullptr)
|
||||
LogDefault(main_domain,
|
||||
"Found db_file setting without "
|
||||
"music_directory - disabling database");
|
||||
return true;
|
||||
}
|
||||
|
||||
struct config_param *allocated = nullptr;
|
||||
|
||||
if (param == nullptr && path != nullptr) {
|
||||
allocated = new config_param("database", path->line);
|
||||
allocated->AddBlockParam("path", path->value.c_str(),
|
||||
path->line);
|
||||
param = allocated;
|
||||
} else {
|
||||
if (IsStorageConfigured())
|
||||
LogDefault(config_domain,
|
||||
"Ignoring the storage configuration "
|
||||
"because the database does not need it");
|
||||
}
|
||||
|
||||
if (param == nullptr)
|
||||
if (!instance->database->Open(error))
|
||||
FatalError(error);
|
||||
|
||||
if (!instance->database->IsPlugin(simple_db_plugin))
|
||||
return true;
|
||||
|
||||
Error error;
|
||||
if (!DatabaseGlobalInit(*param, error))
|
||||
FatalError(error);
|
||||
|
||||
delete allocated;
|
||||
|
||||
if (!DatabaseGlobalOpen(error))
|
||||
FatalError(error);
|
||||
SimpleDatabase &db = *(SimpleDatabase *)instance->database;
|
||||
instance->update = new UpdateService(*instance->event_loop, db,
|
||||
static_cast<CompositeStorage &>(*instance->storage),
|
||||
*instance);
|
||||
|
||||
/* run database update after daemonization? */
|
||||
return !db_is_simple() || db_exists();
|
||||
return db.FileExists();
|
||||
}
|
||||
|
||||
static bool
|
||||
InitDatabaseAndStorage()
|
||||
{
|
||||
const bool create_db = !glue_db_init_and_load();
|
||||
return create_db;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Configure and initialize the sticker subsystem.
|
||||
*/
|
||||
@@ -224,11 +265,27 @@ static bool
|
||||
glue_state_file_init(Error &error)
|
||||
{
|
||||
auto path_fs = config_get_path(CONF_STATE_FILE, error);
|
||||
if (path_fs.IsNull())
|
||||
return !error.IsDefined();
|
||||
if (path_fs.IsNull()) {
|
||||
if (error.IsDefined())
|
||||
return false;
|
||||
|
||||
state_file = new StateFile(std::move(path_fs),
|
||||
*instance->partition, *main_loop);
|
||||
#ifdef ANDROID
|
||||
const auto cache_dir = GetUserCacheDir();
|
||||
if (cache_dir.IsNull())
|
||||
return true;
|
||||
|
||||
path_fs = AllocatedPath::Build(cache_dir, "state");
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned interval = config_get_unsigned(CONF_STATE_FILE_INTERVAL,
|
||||
StateFile::DEFAULT_INTERVAL);
|
||||
|
||||
state_file = new StateFile(std::move(path_fs), interval,
|
||||
*instance->partition,
|
||||
*instance->event_loop);
|
||||
state_file->Read();
|
||||
return true;
|
||||
}
|
||||
@@ -240,9 +297,8 @@ static void winsock_init(void)
|
||||
{
|
||||
#ifdef WIN32
|
||||
WSADATA sockinfo;
|
||||
int retval;
|
||||
|
||||
retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
|
||||
int retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
|
||||
if(retval != 0)
|
||||
FormatFatalError("Attempt to open Winsock2 failed; error code %d",
|
||||
retval);
|
||||
@@ -260,14 +316,11 @@ static void
|
||||
initialize_decoder_and_player(void)
|
||||
{
|
||||
const struct config_param *param;
|
||||
char *test;
|
||||
size_t buffer_size;
|
||||
float perc;
|
||||
unsigned buffered_chunks;
|
||||
unsigned buffered_before_play;
|
||||
|
||||
size_t buffer_size;
|
||||
param = config_get_param(CONF_AUDIO_BUFFER_SIZE);
|
||||
if (param != nullptr) {
|
||||
char *test;
|
||||
long tmp = strtol(param->value.c_str(), &test, 10);
|
||||
if (*test != '\0' || tmp <= 0 || tmp == LONG_MAX)
|
||||
FormatFatalError("buffer size \"%s\" is not a "
|
||||
@@ -279,14 +332,16 @@ initialize_decoder_and_player(void)
|
||||
|
||||
buffer_size *= 1024;
|
||||
|
||||
buffered_chunks = buffer_size / CHUNK_SIZE;
|
||||
const unsigned buffered_chunks = buffer_size / CHUNK_SIZE;
|
||||
|
||||
if (buffered_chunks >= 1 << 15)
|
||||
FormatFatalError("buffer size \"%lu\" is too big",
|
||||
(unsigned long)buffer_size);
|
||||
|
||||
float perc;
|
||||
param = config_get_param(CONF_BUFFER_BEFORE_PLAY);
|
||||
if (param != nullptr) {
|
||||
char *test;
|
||||
perc = strtod(param->value.c_str(), &test);
|
||||
if (*test != '%' || perc < 0 || perc > 100) {
|
||||
FormatFatalError("buffered before play \"%s\" is not "
|
||||
@@ -297,7 +352,7 @@ initialize_decoder_and_player(void)
|
||||
} else
|
||||
perc = DEFAULT_BUFFER_BEFORE_PLAY;
|
||||
|
||||
buffered_before_play = (perc / 100) * buffered_chunks;
|
||||
unsigned buffered_before_play = (perc / 100) * buffered_chunks;
|
||||
if (buffered_before_play > buffered_chunks)
|
||||
buffered_before_play = buffered_chunks;
|
||||
|
||||
@@ -336,11 +391,13 @@ idle_event_emitted(void)
|
||||
static void
|
||||
shutdown_event_emitted(void)
|
||||
{
|
||||
main_loop->Break();
|
||||
instance->event_loop->Break();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef WIN32
|
||||
@@ -350,14 +407,19 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int mpd_main_after_fork(struct options);
|
||||
|
||||
#ifdef ANDROID
|
||||
static inline
|
||||
#endif
|
||||
int mpd_main(int argc, char *argv[])
|
||||
{
|
||||
struct options options;
|
||||
clock_t start;
|
||||
bool create_db;
|
||||
Error error;
|
||||
bool success;
|
||||
|
||||
#ifndef ANDROID
|
||||
daemonize_close_stdin();
|
||||
|
||||
#ifdef HAVE_LOCALE_H
|
||||
@@ -365,19 +427,43 @@ int mpd_main(int argc, char *argv[])
|
||||
setlocale(LC_CTYPE,"");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
g_set_application_name("Music Player Daemon");
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2,32,0)
|
||||
/* enable GLib's thread safety code */
|
||||
g_thread_init(nullptr);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
io_thread_init();
|
||||
if (!IcuInit(error)) {
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
winsock_init();
|
||||
io_thread_init();
|
||||
config_global_init();
|
||||
|
||||
success = parse_cmdline(argc, argv, &options, error);
|
||||
if (!success) {
|
||||
#ifdef ANDROID
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
{
|
||||
const auto sdcard = Environment::getExternalStorageDirectory();
|
||||
if (!sdcard.IsNull()) {
|
||||
const auto config_path =
|
||||
AllocatedPath::Build(sdcard, "mpd.conf");
|
||||
if (FileExists(config_path) &&
|
||||
!ReadConfigFile(config_path, error)) {
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (!parse_cmdline(argc, argv, &options, error)) {
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -386,6 +472,7 @@ int mpd_main(int argc, char *argv[])
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
stats_global_init();
|
||||
TagLoadConfig();
|
||||
@@ -395,23 +482,60 @@ int mpd_main(int argc, char *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
main_thread = ThreadId::GetCurrent();
|
||||
main_loop = new EventLoop(EventLoop::Default());
|
||||
|
||||
instance = new Instance();
|
||||
instance->event_loop = new EventLoop();
|
||||
|
||||
const unsigned max_clients = config_get_positive(CONF_MAX_CONN, 10);
|
||||
instance->client_list = new ClientList(max_clients);
|
||||
|
||||
success = listen_global_init(error);
|
||||
if (!success) {
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
instance->neighbors = new NeighborGlue();
|
||||
if (!instance->neighbors->Init(io_thread_get(), *instance, error)) {
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
daemonize_set_user();
|
||||
if (instance->neighbors->IsEmpty()) {
|
||||
delete instance->neighbors;
|
||||
instance->neighbors = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
GlobalEvents::Initialize(*main_loop);
|
||||
const unsigned max_clients = config_get_positive(CONF_MAX_CONN, 10);
|
||||
instance->client_list = new ClientList(max_clients);
|
||||
|
||||
initialize_decoder_and_player();
|
||||
|
||||
if (!listen_global_init(*instance->event_loop, *instance->partition,
|
||||
error)) {
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#ifndef ANDROID
|
||||
daemonize_set_user();
|
||||
daemonize_begin(options.daemon);
|
||||
#endif
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
/* Runs the OS X native event loop in the main thread, and runs
|
||||
the rest of mpd_main on a new thread. This lets CoreAudio receive
|
||||
route change notifications (e.g. plugging or unplugging headphones).
|
||||
All hardware output on OS X ultimately uses CoreAudio internally.
|
||||
This must be run after forking; if dispatch is called before forking,
|
||||
the child process will have a broken internal dispatch state. */
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
exit(mpd_main_after_fork(options));
|
||||
});
|
||||
dispatch_main();
|
||||
return EXIT_FAILURE; // unreachable, because dispatch_main never returns
|
||||
#else
|
||||
return mpd_main_after_fork(options);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int mpd_main_after_fork(struct options options)
|
||||
{
|
||||
Error error;
|
||||
|
||||
GlobalEvents::Initialize(*instance->event_loop);
|
||||
GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted);
|
||||
#ifdef WIN32
|
||||
GlobalEvents::Register(GlobalEvents::SHUTDOWN, shutdown_event_emitted);
|
||||
@@ -431,23 +555,23 @@ int mpd_main(int argc, char *argv[])
|
||||
archive_plugin_init_all();
|
||||
#endif
|
||||
|
||||
if (!pcm_resample_global_init(error)) {
|
||||
if (!pcm_convert_global_init(error)) {
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
decoder_plugin_init_all();
|
||||
update_global_init();
|
||||
|
||||
create_db = !glue_db_init_and_load();
|
||||
#ifdef ENABLE_DATABASE
|
||||
const bool create_db = InitDatabaseAndStorage();
|
||||
#endif
|
||||
|
||||
glue_sticker_init();
|
||||
|
||||
command_init();
|
||||
initialize_decoder_and_player();
|
||||
volume_init();
|
||||
initAudioConfig();
|
||||
audio_output_all_init(instance->partition->pc);
|
||||
instance->partition->outputs.Configure(*instance->event_loop,
|
||||
instance->partition->pc);
|
||||
client_manager_init();
|
||||
replay_gain_global_init();
|
||||
|
||||
@@ -458,43 +582,59 @@ int mpd_main(int argc, char *argv[])
|
||||
|
||||
playlist_list_global_init();
|
||||
|
||||
daemonize(options.daemon);
|
||||
#ifndef ANDROID
|
||||
daemonize_commit();
|
||||
|
||||
setup_log_output(options.log_stderr);
|
||||
|
||||
SignalHandlersInit(*main_loop);
|
||||
SignalHandlersInit(*instance->event_loop);
|
||||
#endif
|
||||
|
||||
io_thread_start();
|
||||
|
||||
ZeroconfInit(*main_loop);
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
if (instance->neighbors != nullptr &&
|
||||
!instance->neighbors->Open(error))
|
||||
FatalError(error);
|
||||
#endif
|
||||
|
||||
player_create(instance->partition->pc);
|
||||
ZeroconfInit(*instance->event_loop);
|
||||
|
||||
StartPlayerThread(instance->partition->pc);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (create_db) {
|
||||
/* the database failed to load: recreate the
|
||||
database */
|
||||
unsigned job = update_enqueue("", true);
|
||||
unsigned job = instance->update->Enqueue("", true);
|
||||
if (job == 0)
|
||||
FatalError("directory update failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!glue_state_file_init(error)) {
|
||||
g_printerr("%s\n", error.GetMessage());
|
||||
LogError(error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(instance->partition->playlist.queue.random));
|
||||
instance->partition->outputs.SetReplayGainMode(replay_gain_get_real_mode(instance->partition->playlist.queue.random));
|
||||
|
||||
success = config_get_bool(CONF_AUTO_UPDATE, false);
|
||||
#ifdef ENABLE_DATABASE
|
||||
if (config_get_bool(CONF_AUTO_UPDATE, false)) {
|
||||
#ifdef ENABLE_INOTIFY
|
||||
if (success && mapper_has_music_directory())
|
||||
mpd_inotify_init(config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
||||
G_MAXUINT));
|
||||
if (instance->storage != nullptr &&
|
||||
instance->update != nullptr)
|
||||
mpd_inotify_init(*instance->event_loop,
|
||||
*instance->storage,
|
||||
*instance->update,
|
||||
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
||||
INT_MAX));
|
||||
#else
|
||||
if (success)
|
||||
FormatWarning(main_domain,
|
||||
FormatWarning(config_domain,
|
||||
"inotify: auto_update was disabled. enable during compilation phase");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
config_global_check();
|
||||
|
||||
@@ -506,8 +646,12 @@ int mpd_main(int argc, char *argv[])
|
||||
win32_app_started();
|
||||
#endif
|
||||
|
||||
/* the MPD frontend does not care about timer slack; set it to
|
||||
a huge value to allow the kernel to reduce CPU wakeups */
|
||||
SetThreadTimerSlackMS(100);
|
||||
|
||||
/* run the main loop */
|
||||
main_loop->Run();
|
||||
instance->event_loop->Run();
|
||||
|
||||
#ifdef WIN32
|
||||
win32_app_stopping();
|
||||
@@ -515,8 +659,11 @@ int mpd_main(int argc, char *argv[])
|
||||
|
||||
/* cleanup */
|
||||
|
||||
#ifdef ENABLE_INOTIFY
|
||||
#if defined(ENABLE_DATABASE) && defined(ENABLE_INOTIFY)
|
||||
mpd_inotify_finish();
|
||||
|
||||
if (instance->update != nullptr)
|
||||
instance->update->CancelAllAsync();
|
||||
#endif
|
||||
|
||||
if (state_file != nullptr) {
|
||||
@@ -529,11 +676,23 @@ int mpd_main(int argc, char *argv[])
|
||||
listen_global_finish();
|
||||
delete instance->client_list;
|
||||
|
||||
start = clock();
|
||||
DatabaseGlobalDeinit();
|
||||
FormatDebug(main_domain,
|
||||
"db_finish took %f seconds",
|
||||
((float)(clock()-start))/CLOCKS_PER_SEC);
|
||||
#ifdef ENABLE_NEIGHBOR_PLUGINS
|
||||
if (instance->neighbors != nullptr) {
|
||||
instance->neighbors->Close();
|
||||
delete instance->neighbors;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
delete instance->update;
|
||||
|
||||
if (instance->database != nullptr) {
|
||||
instance->database->Close();
|
||||
delete instance->database;
|
||||
}
|
||||
|
||||
delete instance->storage;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SQLITE
|
||||
sticker_global_finish();
|
||||
@@ -543,27 +702,62 @@ int mpd_main(int argc, char *argv[])
|
||||
|
||||
playlist_list_global_finish();
|
||||
input_stream_global_finish();
|
||||
audio_output_all_finish();
|
||||
volume_finish();
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
mapper_finish();
|
||||
#endif
|
||||
|
||||
delete instance->partition;
|
||||
command_finish();
|
||||
update_global_finish();
|
||||
decoder_plugin_deinit_all();
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
archive_plugin_deinit_all();
|
||||
#endif
|
||||
config_global_finish();
|
||||
stats_global_finish();
|
||||
io_thread_deinit();
|
||||
#ifndef ANDROID
|
||||
SignalHandlersFinish();
|
||||
#endif
|
||||
delete instance->event_loop;
|
||||
delete instance;
|
||||
delete main_loop;
|
||||
instance = nullptr;
|
||||
#ifndef ANDROID
|
||||
daemonize_finish();
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
IcuFinish();
|
||||
|
||||
log_deinit();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
gcc_visibility_default
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context)
|
||||
{
|
||||
Java::Init(env);
|
||||
Java::File::Initialise(env);
|
||||
Environment::Initialise(env);
|
||||
|
||||
context = new Context(env, _context);
|
||||
|
||||
mpd_main(0, nullptr);
|
||||
|
||||
delete context;
|
||||
Environment::Deinitialise(env);
|
||||
}
|
||||
|
||||
gcc_visibility_default
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_musicpd_Bridge_shutdown(JNIEnv *, jclass)
|
||||
{
|
||||
if (instance != nullptr)
|
||||
instance->event_loop->Break();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
14
src/Main.hxx
14
src/Main.hxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,16 +20,18 @@
|
||||
#ifndef MPD_MAIN_HXX
|
||||
#define MPD_MAIN_HXX
|
||||
|
||||
class ThreadId;
|
||||
class EventLoop;
|
||||
class Context;
|
||||
struct Instance;
|
||||
|
||||
extern ThreadId main_thread;
|
||||
|
||||
extern EventLoop *main_loop;
|
||||
#ifdef ANDROID
|
||||
extern Context *context;
|
||||
#endif
|
||||
|
||||
extern Instance *instance;
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
/**
|
||||
* A entry point for application.
|
||||
* On non-Windows platforms this is called directly from main()
|
||||
@@ -38,6 +40,8 @@ extern Instance *instance;
|
||||
*/
|
||||
int mpd_main(int argc, char *argv[]);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
/**
|
||||
|
||||
187
src/Mapper.cxx
187
src/Mapper.cxx
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,36 +23,18 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "Mapper.hxx"
|
||||
#include "Directory.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/Charset.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/DirectoryReader.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "fs/CheckFile.hxx"
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "Main.hxx"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static constexpr Domain mapper_domain("mapper");
|
||||
|
||||
/**
|
||||
* The absolute path of the music directory encoded in UTF-8.
|
||||
*/
|
||||
static std::string music_dir_utf8;
|
||||
static size_t music_dir_utf8_length;
|
||||
|
||||
/**
|
||||
* The absolute path of the music directory encoded in the filesystem
|
||||
* character set.
|
||||
*/
|
||||
static AllocatedPath music_dir_fs = AllocatedPath::Null();
|
||||
|
||||
/**
|
||||
* The absolute path of the playlist directory encoded in the
|
||||
@@ -60,51 +42,6 @@ static AllocatedPath music_dir_fs = AllocatedPath::Null();
|
||||
*/
|
||||
static AllocatedPath playlist_dir_fs = AllocatedPath::Null();
|
||||
|
||||
static void
|
||||
check_directory(const char *path_utf8, const AllocatedPath &path_fs)
|
||||
{
|
||||
struct stat st;
|
||||
if (!StatFile(path_fs, st)) {
|
||||
FormatErrno(mapper_domain,
|
||||
"Failed to stat directory \"%s\"",
|
||||
path_utf8);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
FormatError(mapper_domain,
|
||||
"Not a directory: %s", path_utf8);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
const auto x = AllocatedPath::Build(path_fs, ".");
|
||||
if (!StatFile(x, st) && errno == EACCES)
|
||||
FormatError(mapper_domain,
|
||||
"No permission to traverse (\"execute\") directory: %s",
|
||||
path_utf8);
|
||||
#endif
|
||||
|
||||
const DirectoryReader reader(path_fs);
|
||||
if (reader.HasFailed() && errno == EACCES)
|
||||
FormatError(mapper_domain,
|
||||
"No permission to read directory: %s", path_utf8);
|
||||
}
|
||||
|
||||
static void
|
||||
mapper_set_music_dir(AllocatedPath &&path)
|
||||
{
|
||||
assert(!path.IsNull());
|
||||
|
||||
music_dir_fs = std::move(path);
|
||||
music_dir_fs.ChopSeparators();
|
||||
|
||||
music_dir_utf8 = music_dir_fs.ToUTF8();
|
||||
music_dir_utf8_length = music_dir_utf8.length();
|
||||
|
||||
check_directory(music_dir_utf8.c_str(), music_dir_fs);
|
||||
}
|
||||
|
||||
static void
|
||||
mapper_set_playlist_dir(AllocatedPath &&path)
|
||||
{
|
||||
@@ -112,16 +49,12 @@ mapper_set_playlist_dir(AllocatedPath &&path)
|
||||
|
||||
playlist_dir_fs = std::move(path);
|
||||
|
||||
const auto utf8 = playlist_dir_fs.ToUTF8();
|
||||
check_directory(utf8.c_str(), playlist_dir_fs);
|
||||
CheckDirectoryReadable(playlist_dir_fs);
|
||||
}
|
||||
|
||||
void
|
||||
mapper_init(AllocatedPath &&_music_dir, AllocatedPath &&_playlist_dir)
|
||||
mapper_init(AllocatedPath &&_playlist_dir)
|
||||
{
|
||||
if (!_music_dir.IsNull())
|
||||
mapper_set_music_dir(std::move(_music_dir));
|
||||
|
||||
if (!_playlist_dir.IsNull())
|
||||
mapper_set_playlist_dir(std::move(_playlist_dir));
|
||||
}
|
||||
@@ -130,30 +63,7 @@ void mapper_finish(void)
|
||||
{
|
||||
}
|
||||
|
||||
const char *
|
||||
mapper_get_music_directory_utf8(void)
|
||||
{
|
||||
return music_dir_utf8.empty()
|
||||
? nullptr
|
||||
: music_dir_utf8.c_str();
|
||||
}
|
||||
|
||||
const AllocatedPath &
|
||||
mapper_get_music_directory_fs(void)
|
||||
{
|
||||
return music_dir_fs;
|
||||
}
|
||||
|
||||
const char *
|
||||
map_to_relative_path(const char *path_utf8)
|
||||
{
|
||||
return !music_dir_utf8.empty() &&
|
||||
memcmp(path_utf8, music_dir_utf8.c_str(),
|
||||
music_dir_utf8_length) == 0 &&
|
||||
PathTraits::IsSeparatorUTF8(path_utf8[music_dir_utf8_length])
|
||||
? path_utf8 + music_dir_utf8_length + 1
|
||||
: path_utf8;
|
||||
}
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
AllocatedPath
|
||||
map_uri_fs(const char *uri)
|
||||
@@ -161,6 +71,10 @@ map_uri_fs(const char *uri)
|
||||
assert(uri != nullptr);
|
||||
assert(*uri != '/');
|
||||
|
||||
if (instance->storage == nullptr)
|
||||
return AllocatedPath::Null();
|
||||
|
||||
const auto music_dir_fs = instance->storage->MapFS("");
|
||||
if (music_dir_fs.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
|
||||
@@ -171,70 +85,17 @@ map_uri_fs(const char *uri)
|
||||
return AllocatedPath::Build(music_dir_fs, uri_fs);
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
map_directory_fs(const Directory &directory)
|
||||
{
|
||||
assert(!music_dir_fs.IsNull());
|
||||
|
||||
if (directory.IsRoot())
|
||||
return music_dir_fs;
|
||||
|
||||
return map_uri_fs(directory.GetPath());
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
map_directory_child_fs(const Directory &directory, const char *name)
|
||||
{
|
||||
assert(!music_dir_fs.IsNull());
|
||||
|
||||
/* check for invalid or unauthorized base names */
|
||||
if (*name == 0 || strchr(name, '/') != nullptr ||
|
||||
strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
|
||||
return AllocatedPath::Null();
|
||||
|
||||
const auto parent_fs = map_directory_fs(directory);
|
||||
if (parent_fs.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
|
||||
const auto name_fs = AllocatedPath::FromUTF8(name);
|
||||
if (name_fs.IsNull())
|
||||
return AllocatedPath::Null();
|
||||
|
||||
return AllocatedPath::Build(parent_fs, name_fs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a song object that was created by song_dup_detached(). It does
|
||||
* not have a real parent directory, only the dummy object
|
||||
* #detached_root.
|
||||
*/
|
||||
static AllocatedPath
|
||||
map_detached_song_fs(const char *uri_utf8)
|
||||
{
|
||||
auto uri_fs = AllocatedPath::FromUTF8(uri_utf8);
|
||||
if (uri_fs.IsNull())
|
||||
return uri_fs;
|
||||
|
||||
return AllocatedPath::Build(music_dir_fs, uri_fs);
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
map_song_fs(const Song &song)
|
||||
{
|
||||
assert(song.IsFile());
|
||||
|
||||
if (song.IsInDatabase())
|
||||
return song.IsDetached()
|
||||
? map_detached_song_fs(song.uri)
|
||||
: map_directory_child_fs(*song.parent, song.uri);
|
||||
else
|
||||
return AllocatedPath::FromUTF8(song.uri);
|
||||
}
|
||||
|
||||
std::string
|
||||
map_fs_to_utf8(const char *path_fs)
|
||||
{
|
||||
if (PathTraits::IsSeparatorFS(path_fs[0])) {
|
||||
if (PathTraitsFS::IsSeparator(path_fs[0])) {
|
||||
if (instance->storage == nullptr)
|
||||
return std::string();
|
||||
|
||||
const auto music_dir_fs = instance->storage->MapFS("");
|
||||
if (music_dir_fs.IsNull())
|
||||
return std::string();
|
||||
|
||||
path_fs = music_dir_fs.RelativeFS(path_fs);
|
||||
if (path_fs == nullptr || *path_fs == 0)
|
||||
return std::string();
|
||||
@@ -243,6 +104,8 @@ map_fs_to_utf8(const char *path_fs)
|
||||
return PathToUTF8(path_fs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
const AllocatedPath &
|
||||
map_spl_path(void)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,50 +30,14 @@
|
||||
|
||||
#define PLAYLIST_FILE_SUFFIX ".m3u"
|
||||
|
||||
class Path;
|
||||
class AllocatedPath;
|
||||
struct Directory;
|
||||
struct Song;
|
||||
|
||||
void
|
||||
mapper_init(AllocatedPath &&music_dir, AllocatedPath &&playlist_dir);
|
||||
mapper_init(AllocatedPath &&playlist_dir);
|
||||
|
||||
void mapper_finish(void);
|
||||
|
||||
/**
|
||||
* Return the absolute path of the music directory encoded in UTF-8 or
|
||||
* nullptr if no music directory was configured.
|
||||
*/
|
||||
gcc_const
|
||||
const char *
|
||||
mapper_get_music_directory_utf8(void);
|
||||
|
||||
/**
|
||||
* Return the absolute path of the music directory encoded in the
|
||||
* filesystem character set.
|
||||
*/
|
||||
gcc_const
|
||||
const AllocatedPath &
|
||||
mapper_get_music_directory_fs(void);
|
||||
|
||||
/**
|
||||
* Returns true if a music directory was configured.
|
||||
*/
|
||||
gcc_const
|
||||
static inline bool
|
||||
mapper_has_music_directory(void)
|
||||
{
|
||||
return mapper_get_music_directory_utf8() != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the specified absolute path points inside the music directory,
|
||||
* this function converts it to a relative path. If not, it returns
|
||||
* the unmodified string pointer.
|
||||
*/
|
||||
gcc_pure
|
||||
const char *
|
||||
map_to_relative_path(const char *path_utf8);
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
/**
|
||||
* Determines the absolute file system path of a relative URI. This
|
||||
@@ -84,39 +48,6 @@ gcc_pure
|
||||
AllocatedPath
|
||||
map_uri_fs(const char *uri);
|
||||
|
||||
/**
|
||||
* Determines the file system path of a directory object.
|
||||
*
|
||||
* @param directory the directory object
|
||||
* @return the path in file system encoding, or nullptr if mapping failed
|
||||
*/
|
||||
gcc_pure
|
||||
AllocatedPath
|
||||
map_directory_fs(const Directory &directory);
|
||||
|
||||
/**
|
||||
* Determines the file system path of a directory's child (may be a
|
||||
* sub directory or a song).
|
||||
*
|
||||
* @param directory the parent directory object
|
||||
* @param name the child's name in UTF-8
|
||||
* @return the path in file system encoding, or nullptr if mapping failed
|
||||
*/
|
||||
gcc_pure
|
||||
AllocatedPath
|
||||
map_directory_child_fs(const Directory &directory, const char *name);
|
||||
|
||||
/**
|
||||
* Determines the file system path of a song. This must not be a
|
||||
* remote song.
|
||||
*
|
||||
* @param song the song object
|
||||
* @return the path in file system encoding, or nullptr if mapping failed
|
||||
*/
|
||||
gcc_pure
|
||||
AllocatedPath
|
||||
map_song_fs(const Song &song);
|
||||
|
||||
/**
|
||||
* Maps a file system path (relative to the music directory or
|
||||
* absolute) to a relative path in UTF-8 encoding.
|
||||
@@ -129,6 +60,8 @@ gcc_pure
|
||||
std::string
|
||||
map_fs_to_utf8(const char *path_fs);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the playlist directory.
|
||||
*/
|
||||
@@ -138,8 +71,7 @@ map_spl_path(void);
|
||||
|
||||
/**
|
||||
* Maps a playlist name (without the ".m3u" suffix) to a file system
|
||||
* path. The return value is allocated on the heap and must be freed
|
||||
* with g_free().
|
||||
* path.
|
||||
*
|
||||
* @return the path in file system encoding, or nullptr if mapping failed
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* Functions which affect the mixers of all audio outputs.
|
||||
*/
|
||||
|
||||
#ifndef MPD_MIXER_ALL_HXX
|
||||
#define MPD_MIXER_ALL_HXX
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
/**
|
||||
* Returns the average volume of all available mixers (range 0..100).
|
||||
* Returns -1 if no mixer can be queried.
|
||||
*/
|
||||
gcc_pure
|
||||
int
|
||||
mixer_all_get_volume(void);
|
||||
|
||||
/**
|
||||
* Sets the volume on all available mixers.
|
||||
*
|
||||
* @param volume the volume (range 0..100)
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool
|
||||
mixer_all_set_volume(unsigned volume);
|
||||
|
||||
/**
|
||||
* Similar to mixer_all_get_volume(), but gets the volume only for
|
||||
* software mixers. See #software_mixer_plugin. This function fails
|
||||
* if no software mixer is configured.
|
||||
*/
|
||||
gcc_pure
|
||||
int
|
||||
mixer_all_get_software_volume(void);
|
||||
|
||||
/**
|
||||
* Similar to mixer_all_set_volume(), but sets the volume only for
|
||||
* software mixers. See #software_mixer_plugin. This function cannot
|
||||
* fail, because the underlying software mixers cannot fail either.
|
||||
*/
|
||||
void
|
||||
mixer_all_set_software_volume(unsigned volume);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -30,7 +30,7 @@ MusicBuffer::MusicBuffer(unsigned num_chunks)
|
||||
FatalError("Failed to allocate buffer");
|
||||
}
|
||||
|
||||
music_chunk *
|
||||
MusicChunk *
|
||||
MusicBuffer::Allocate()
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
@@ -38,7 +38,7 @@ MusicBuffer::Allocate()
|
||||
}
|
||||
|
||||
void
|
||||
MusicBuffer::Return(music_chunk *chunk)
|
||||
MusicBuffer::Return(MusicChunk *chunk)
|
||||
{
|
||||
assert(chunk != nullptr);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,22 +23,22 @@
|
||||
#include "util/SliceBuffer.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
|
||||
struct music_chunk;
|
||||
struct MusicChunk;
|
||||
|
||||
/**
|
||||
* An allocator for #music_chunk objects.
|
||||
* An allocator for #MusicChunk objects.
|
||||
*/
|
||||
class MusicBuffer {
|
||||
/** a mutex which protects #buffer */
|
||||
Mutex mutex;
|
||||
|
||||
SliceBuffer<music_chunk> buffer;
|
||||
SliceBuffer<MusicChunk> buffer;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new #MusicBuffer object.
|
||||
*
|
||||
* @param num_chunks the number of #music_chunk reserved in
|
||||
* @param num_chunks the number of #MusicChunk reserved in
|
||||
* this buffer
|
||||
*/
|
||||
MusicBuffer(unsigned num_chunks);
|
||||
@@ -71,13 +71,13 @@ public:
|
||||
* @return an empty chunk or nullptr if there are no chunks
|
||||
* available
|
||||
*/
|
||||
music_chunk *Allocate();
|
||||
MusicChunk *Allocate();
|
||||
|
||||
/**
|
||||
* Returns a chunk to the buffer. It can be reused by
|
||||
* Allocate() then.
|
||||
*/
|
||||
void Return(music_chunk *chunk);
|
||||
void Return(MusicChunk *chunk);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -24,14 +24,14 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
music_chunk::~music_chunk()
|
||||
MusicChunk::~MusicChunk()
|
||||
{
|
||||
delete tag;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool
|
||||
music_chunk::CheckFormat(const AudioFormat other_format) const
|
||||
MusicChunk::CheckFormat(const AudioFormat other_format) const
|
||||
{
|
||||
assert(other_format.IsValid());
|
||||
|
||||
@@ -40,34 +40,31 @@ music_chunk::CheckFormat(const AudioFormat other_format) const
|
||||
#endif
|
||||
|
||||
WritableBuffer<void>
|
||||
music_chunk::Write(const AudioFormat af,
|
||||
float data_time, uint16_t _bit_rate)
|
||||
MusicChunk::Write(const AudioFormat af,
|
||||
SongTime data_time, uint16_t _bit_rate)
|
||||
{
|
||||
assert(CheckFormat(af));
|
||||
assert(length == 0 || audio_format.IsValid());
|
||||
|
||||
if (length == 0) {
|
||||
/* if the chunk is empty, nobody has set bitRate and
|
||||
times yet */
|
||||
time yet */
|
||||
|
||||
bit_rate = _bit_rate;
|
||||
times = data_time;
|
||||
}
|
||||
|
||||
const size_t frame_size = af.GetFrameSize();
|
||||
size_t num_frames = (sizeof(data) - length) / frame_size;
|
||||
if (num_frames == 0)
|
||||
return WritableBuffer<void>::Null();
|
||||
time = data_time;
|
||||
|
||||
#ifndef NDEBUG
|
||||
audio_format = af;
|
||||
#endif
|
||||
}
|
||||
|
||||
const size_t frame_size = af.GetFrameSize();
|
||||
size_t num_frames = (sizeof(data) - length) / frame_size;
|
||||
return { data + length, num_frames * frame_size };
|
||||
}
|
||||
|
||||
bool
|
||||
music_chunk::Expand(const AudioFormat af, size_t _length)
|
||||
MusicChunk::Expand(const AudioFormat af, size_t _length)
|
||||
{
|
||||
const size_t frame_size = af.GetFrameSize();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef MPD_MUSIC_CHUNK_HXX
|
||||
#define MPD_MUSIC_CHUNK_HXX
|
||||
|
||||
#include "Chrono.hxx"
|
||||
#include "ReplayGainInfo.hxx"
|
||||
#include "util/WritableBuffer.hxx"
|
||||
|
||||
@@ -39,15 +40,15 @@ struct Tag;
|
||||
* A chunk of music data. Its format is defined by the
|
||||
* MusicPipe::Push() caller.
|
||||
*/
|
||||
struct music_chunk {
|
||||
struct MusicChunk {
|
||||
/** the next chunk in a linked list */
|
||||
struct music_chunk *next;
|
||||
MusicChunk *next;
|
||||
|
||||
/**
|
||||
* An optional chunk which should be mixed into this chunk.
|
||||
* This is used for cross-fading.
|
||||
*/
|
||||
struct music_chunk *other;
|
||||
MusicChunk *other;
|
||||
|
||||
/**
|
||||
* The current mix ratio for cross-fading: 1.0 means play 100%
|
||||
@@ -62,7 +63,7 @@ struct music_chunk {
|
||||
uint16_t bit_rate;
|
||||
|
||||
/** the time stamp within the song */
|
||||
float times;
|
||||
SignedSongTime time;
|
||||
|
||||
/**
|
||||
* An optional tag associated with this chunk (and the
|
||||
@@ -92,13 +93,13 @@ struct music_chunk {
|
||||
AudioFormat audio_format;
|
||||
#endif
|
||||
|
||||
music_chunk()
|
||||
MusicChunk()
|
||||
:other(nullptr),
|
||||
length(0),
|
||||
tag(nullptr),
|
||||
replay_gain_serial(0) {}
|
||||
|
||||
~music_chunk();
|
||||
~MusicChunk();
|
||||
|
||||
bool IsEmpty() const {
|
||||
return length == 0 && tag == nullptr;
|
||||
@@ -116,9 +117,9 @@ struct music_chunk {
|
||||
/**
|
||||
* Prepares appending to the music chunk. Returns a buffer
|
||||
* where you may write into. After you are finished, call
|
||||
* music_chunk_expand().
|
||||
* Expand().
|
||||
*
|
||||
* @param chunk the music_chunk object
|
||||
* @param chunk the MusicChunk object
|
||||
* @param audio_format the audio format for the appended data;
|
||||
* must stay the same for the life cycle of this chunk
|
||||
* @param data_time the time within the song
|
||||
@@ -128,13 +129,14 @@ struct music_chunk {
|
||||
* @return a writable buffer, or nullptr if the chunk is full
|
||||
*/
|
||||
WritableBuffer<void> Write(AudioFormat af,
|
||||
float data_time, uint16_t bit_rate);
|
||||
SongTime data_time,
|
||||
uint16_t bit_rate);
|
||||
|
||||
/**
|
||||
* Increases the length of the chunk after the caller has written to
|
||||
* the buffer returned by music_chunk_write().
|
||||
* the buffer returned by Write().
|
||||
*
|
||||
* @param chunk the music_chunk object
|
||||
* @param chunk the MusicChunk object
|
||||
* @param audio_format the audio format for the appended data; must
|
||||
* stay the same for the life cycle of this chunk
|
||||
* @param length the number of bytes which were appended
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -25,11 +25,11 @@
|
||||
#ifndef NDEBUG
|
||||
|
||||
bool
|
||||
MusicPipe::Contains(const music_chunk *chunk) const
|
||||
MusicPipe::Contains(const MusicChunk *chunk) const
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
for (const struct music_chunk *i = head; i != nullptr; i = i->next)
|
||||
for (const MusicChunk *i = head; i != nullptr; i = i->next)
|
||||
if (i == chunk)
|
||||
return true;
|
||||
|
||||
@@ -38,12 +38,12 @@ MusicPipe::Contains(const music_chunk *chunk) const
|
||||
|
||||
#endif
|
||||
|
||||
music_chunk *
|
||||
MusicChunk *
|
||||
MusicPipe::Shift()
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
music_chunk *chunk = head;
|
||||
MusicChunk *chunk = head;
|
||||
if (chunk != nullptr) {
|
||||
assert(!chunk->IsEmpty());
|
||||
|
||||
@@ -62,7 +62,7 @@ MusicPipe::Shift()
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* poison the "next" reference */
|
||||
chunk->next = (music_chunk *)(void *)0x01010101;
|
||||
chunk->next = (MusicChunk *)(void *)0x01010101;
|
||||
|
||||
if (size == 0)
|
||||
audio_format.Clear();
|
||||
@@ -75,14 +75,14 @@ MusicPipe::Shift()
|
||||
void
|
||||
MusicPipe::Clear(MusicBuffer &buffer)
|
||||
{
|
||||
music_chunk *chunk;
|
||||
MusicChunk *chunk;
|
||||
|
||||
while ((chunk = Shift()) != nullptr)
|
||||
buffer.Return(chunk);
|
||||
}
|
||||
|
||||
void
|
||||
MusicPipe::Push(music_chunk *chunk)
|
||||
MusicPipe::Push(MusicChunk *chunk)
|
||||
{
|
||||
assert(!chunk->IsEmpty());
|
||||
assert(chunk->length == 0 || chunk->audio_format.IsValid());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -29,19 +29,19 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
struct music_chunk;
|
||||
struct MusicChunk;
|
||||
class MusicBuffer;
|
||||
|
||||
/**
|
||||
* A queue of #music_chunk objects. One party appends chunks at the
|
||||
* A queue of #MusicChunk objects. One party appends chunks at the
|
||||
* tail, and the other consumes them from the head.
|
||||
*/
|
||||
class MusicPipe {
|
||||
/** the first chunk */
|
||||
music_chunk *head;
|
||||
MusicChunk *head;
|
||||
|
||||
/** a pointer to the tail of the chunk */
|
||||
music_chunk **tail_r;
|
||||
MusicChunk **tail_r;
|
||||
|
||||
/** the current number of chunks */
|
||||
unsigned size;
|
||||
@@ -87,22 +87,22 @@ public:
|
||||
* Checks if the specified chunk is enqueued in the music pipe.
|
||||
*/
|
||||
gcc_pure
|
||||
bool Contains(const music_chunk *chunk) const;
|
||||
bool Contains(const MusicChunk *chunk) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the first #music_chunk from the pipe. Returns
|
||||
* Returns the first #MusicChunk from the pipe. Returns
|
||||
* nullptr if the pipe is empty.
|
||||
*/
|
||||
gcc_pure
|
||||
const music_chunk *Peek() const {
|
||||
const MusicChunk *Peek() const {
|
||||
return head;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first chunk from the head, and returns it.
|
||||
*/
|
||||
music_chunk *Shift();
|
||||
MusicChunk *Shift();
|
||||
|
||||
/**
|
||||
* Clears the whole pipe and returns the chunks to the buffer.
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
/**
|
||||
* Pushes a chunk to the tail of the pipe.
|
||||
*/
|
||||
void Push(music_chunk *chunk);
|
||||
void Push(MusicChunk *chunk);
|
||||
|
||||
/**
|
||||
* Returns the number of chunks currently in this pipe.
|
||||
|
||||
@@ -1,594 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "OutputAll.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "OutputInternal.hxx"
|
||||
#include "OutputControl.hxx"
|
||||
#include "OutputError.hxx"
|
||||
#include "MusicBuffer.hxx"
|
||||
#include "MusicPipe.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "ConfigData.hxx"
|
||||
#include "ConfigGlobal.hxx"
|
||||
#include "ConfigOption.hxx"
|
||||
#include "notify.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static AudioFormat input_audio_format;
|
||||
|
||||
static struct audio_output **audio_outputs;
|
||||
static unsigned int num_audio_outputs;
|
||||
|
||||
/**
|
||||
* The #MusicBuffer object where consumed chunks are returned.
|
||||
*/
|
||||
static MusicBuffer *g_music_buffer;
|
||||
|
||||
/**
|
||||
* The #MusicPipe object which feeds all audio outputs. It is filled
|
||||
* by audio_output_all_play().
|
||||
*/
|
||||
static MusicPipe *g_mp;
|
||||
|
||||
/**
|
||||
* The "elapsed_time" stamp of the most recently finished chunk.
|
||||
*/
|
||||
static float audio_output_all_elapsed_time = -1.0;
|
||||
|
||||
unsigned int audio_output_count(void)
|
||||
{
|
||||
return num_audio_outputs;
|
||||
}
|
||||
|
||||
struct audio_output *
|
||||
audio_output_get(unsigned i)
|
||||
{
|
||||
assert(i < num_audio_outputs);
|
||||
|
||||
assert(audio_outputs[i] != nullptr);
|
||||
|
||||
return audio_outputs[i];
|
||||
}
|
||||
|
||||
struct audio_output *
|
||||
audio_output_find(const char *name)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||
struct audio_output *ao = audio_output_get(i);
|
||||
|
||||
if (strcmp(ao->name, name) == 0)
|
||||
return ao;
|
||||
}
|
||||
|
||||
/* name not found */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gcc_const
|
||||
static unsigned
|
||||
audio_output_config_count(void)
|
||||
{
|
||||
unsigned int nr = 0;
|
||||
const struct config_param *param = nullptr;
|
||||
|
||||
while ((param = config_get_next_param(CONF_AUDIO_OUTPUT, param)))
|
||||
nr++;
|
||||
if (!nr)
|
||||
nr = 1; /* we'll always have at least one device */
|
||||
return nr;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_init(PlayerControl &pc)
|
||||
{
|
||||
const struct config_param *param = nullptr;
|
||||
unsigned int i;
|
||||
Error error;
|
||||
|
||||
num_audio_outputs = audio_output_config_count();
|
||||
audio_outputs = g_new(struct audio_output *, num_audio_outputs);
|
||||
|
||||
const config_param empty;
|
||||
|
||||
for (i = 0; i < num_audio_outputs; i++)
|
||||
{
|
||||
unsigned int j;
|
||||
|
||||
param = config_get_next_param(CONF_AUDIO_OUTPUT, param);
|
||||
if (param == nullptr) {
|
||||
/* only allow param to be nullptr if there
|
||||
just one audio output */
|
||||
assert(i == 0);
|
||||
assert(num_audio_outputs == 1);
|
||||
|
||||
param = ∅
|
||||
}
|
||||
|
||||
audio_output *output = audio_output_new(*param, pc, error);
|
||||
if (output == nullptr) {
|
||||
if (param != nullptr)
|
||||
FormatFatalError("line %i: %s",
|
||||
param->line,
|
||||
error.GetMessage());
|
||||
else
|
||||
FatalError(error);
|
||||
}
|
||||
|
||||
audio_outputs[i] = output;
|
||||
|
||||
/* require output names to be unique: */
|
||||
for (j = 0; j < i; j++) {
|
||||
if (!strcmp(output->name, audio_outputs[j]->name)) {
|
||||
FormatFatalError("output devices with identical "
|
||||
"names: %s", output->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_finish(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_audio_outputs; i++) {
|
||||
audio_output_disable(audio_outputs[i]);
|
||||
audio_output_finish(audio_outputs[i]);
|
||||
}
|
||||
|
||||
g_free(audio_outputs);
|
||||
audio_outputs = nullptr;
|
||||
num_audio_outputs = 0;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_enable_disable(void)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; i++) {
|
||||
struct audio_output *ao = audio_outputs[i];
|
||||
bool enabled;
|
||||
|
||||
ao->mutex.lock();
|
||||
enabled = ao->really_enabled;
|
||||
ao->mutex.unlock();
|
||||
|
||||
if (ao->enabled != enabled) {
|
||||
if (ao->enabled)
|
||||
audio_output_enable(ao);
|
||||
else
|
||||
audio_output_disable(ao);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if all (active) outputs have finished the current
|
||||
* command.
|
||||
*/
|
||||
static bool
|
||||
audio_output_all_finished(void)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||
struct audio_output *ao = audio_outputs[i];
|
||||
|
||||
const ScopeLock protect(ao->mutex);
|
||||
if (audio_output_is_open(ao) &&
|
||||
!audio_output_command_is_finished(ao))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void audio_output_wait_all(void)
|
||||
{
|
||||
while (!audio_output_all_finished())
|
||||
audio_output_client_notify.Wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals all audio outputs which are open.
|
||||
*/
|
||||
static void
|
||||
audio_output_allow_play_all(void)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_allow_play(audio_outputs[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
audio_output_reset_reopen(struct audio_output *ao)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
if (!ao->open && ao->fail_timer != nullptr) {
|
||||
g_timer_destroy(ao->fail_timer);
|
||||
ao->fail_timer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the "reopen" flag on all audio devices. MPD should
|
||||
* immediately retry to open the device instead of waiting for the
|
||||
* timeout when the user wants to start playback.
|
||||
*/
|
||||
static void
|
||||
audio_output_all_reset_reopen(void)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||
struct audio_output *ao = audio_outputs[i];
|
||||
|
||||
audio_output_reset_reopen(ao);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens all output devices which are enabled, but closed.
|
||||
*
|
||||
* @return true if there is at least open output device which is open
|
||||
*/
|
||||
static bool
|
||||
audio_output_all_update(void)
|
||||
{
|
||||
unsigned int i;
|
||||
bool ret = false;
|
||||
|
||||
if (!input_audio_format.IsDefined())
|
||||
return false;
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i)
|
||||
ret = audio_output_update(audio_outputs[i],
|
||||
input_audio_format, *g_mp) || ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_set_replay_gain_mode(ReplayGainMode mode)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_set_replay_gain_mode(audio_outputs[i], mode);
|
||||
}
|
||||
|
||||
bool
|
||||
audio_output_all_play(struct music_chunk *chunk, Error &error)
|
||||
{
|
||||
bool ret;
|
||||
unsigned int i;
|
||||
|
||||
assert(g_music_buffer != nullptr);
|
||||
assert(g_mp != nullptr);
|
||||
assert(chunk != nullptr);
|
||||
assert(chunk->CheckFormat(input_audio_format));
|
||||
|
||||
ret = audio_output_all_update();
|
||||
if (!ret) {
|
||||
/* TODO: obtain real error */
|
||||
error.Set(output_domain, "Failed to open audio output");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_mp->Push(chunk);
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_play(audio_outputs[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
audio_output_all_open(const AudioFormat audio_format,
|
||||
MusicBuffer &buffer,
|
||||
Error &error)
|
||||
{
|
||||
bool ret = false, enabled = false;
|
||||
unsigned int i;
|
||||
|
||||
assert(g_music_buffer == nullptr || g_music_buffer == &buffer);
|
||||
assert((g_mp == nullptr) == (g_music_buffer == nullptr));
|
||||
|
||||
g_music_buffer = &buffer;
|
||||
|
||||
/* the audio format must be the same as existing chunks in the
|
||||
pipe */
|
||||
assert(g_mp == nullptr || g_mp->CheckFormat(audio_format));
|
||||
|
||||
if (g_mp == nullptr)
|
||||
g_mp = new MusicPipe();
|
||||
else
|
||||
/* if the pipe hasn't been cleared, the the audio
|
||||
format must not have changed */
|
||||
assert(g_mp->IsEmpty() || audio_format == input_audio_format);
|
||||
|
||||
input_audio_format = audio_format;
|
||||
|
||||
audio_output_all_reset_reopen();
|
||||
audio_output_all_enable_disable();
|
||||
audio_output_all_update();
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i) {
|
||||
if (audio_outputs[i]->enabled)
|
||||
enabled = true;
|
||||
|
||||
if (audio_outputs[i]->open)
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (!enabled)
|
||||
error.Set(output_domain, "All audio outputs are disabled");
|
||||
else if (!ret)
|
||||
/* TODO: obtain real error */
|
||||
error.Set(output_domain, "Failed to open audio output");
|
||||
|
||||
if (!ret)
|
||||
/* close all devices if there was an error */
|
||||
audio_output_all_close();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the specified audio output already consumed this chunk?
|
||||
*/
|
||||
static bool
|
||||
chunk_is_consumed_in(const struct audio_output *ao,
|
||||
const struct music_chunk *chunk)
|
||||
{
|
||||
if (!ao->open)
|
||||
return true;
|
||||
|
||||
if (ao->chunk == nullptr)
|
||||
return false;
|
||||
|
||||
assert(chunk == ao->chunk || g_mp->Contains(ao->chunk));
|
||||
|
||||
if (chunk != ao->chunk) {
|
||||
assert(chunk->next != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return ao->chunk_finished && chunk->next == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has this chunk been consumed by all audio outputs?
|
||||
*/
|
||||
static bool
|
||||
chunk_is_consumed(const struct music_chunk *chunk)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||
struct audio_output *ao = audio_outputs[i];
|
||||
|
||||
const ScopeLock protect(ao->mutex);
|
||||
if (!chunk_is_consumed_in(ao, chunk))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* There's only one chunk left in the pipe (#g_mp), and all audio
|
||||
* outputs have consumed it already. Clear the reference.
|
||||
*/
|
||||
static void
|
||||
clear_tail_chunk(gcc_unused const struct music_chunk *chunk, bool *locked)
|
||||
{
|
||||
assert(chunk->next == nullptr);
|
||||
assert(g_mp->Contains(chunk));
|
||||
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i) {
|
||||
struct audio_output *ao = audio_outputs[i];
|
||||
|
||||
/* this mutex will be unlocked by the caller when it's
|
||||
ready */
|
||||
ao->mutex.lock();
|
||||
locked[i] = ao->open;
|
||||
|
||||
if (!locked[i]) {
|
||||
ao->mutex.unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(ao->chunk == chunk);
|
||||
assert(ao->chunk_finished);
|
||||
ao->chunk = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
audio_output_all_check(void)
|
||||
{
|
||||
const struct music_chunk *chunk;
|
||||
bool is_tail;
|
||||
struct music_chunk *shifted;
|
||||
bool locked[num_audio_outputs];
|
||||
|
||||
assert(g_music_buffer != nullptr);
|
||||
assert(g_mp != nullptr);
|
||||
|
||||
while ((chunk = g_mp->Peek()) != nullptr) {
|
||||
assert(!g_mp->IsEmpty());
|
||||
|
||||
if (!chunk_is_consumed(chunk))
|
||||
/* at least one output is not finished playing
|
||||
this chunk */
|
||||
return g_mp->GetSize();
|
||||
|
||||
if (chunk->length > 0 && chunk->times >= 0.0)
|
||||
/* only update elapsed_time if the chunk
|
||||
provides a defined value */
|
||||
audio_output_all_elapsed_time = chunk->times;
|
||||
|
||||
is_tail = chunk->next == nullptr;
|
||||
if (is_tail)
|
||||
/* this is the tail of the pipe - clear the
|
||||
chunk reference in all outputs */
|
||||
clear_tail_chunk(chunk, locked);
|
||||
|
||||
/* remove the chunk from the pipe */
|
||||
shifted = g_mp->Shift();
|
||||
assert(shifted == chunk);
|
||||
|
||||
if (is_tail)
|
||||
/* unlock all audio outputs which were locked
|
||||
by clear_tail_chunk() */
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
||||
if (locked[i])
|
||||
audio_outputs[i]->mutex.unlock();
|
||||
|
||||
/* return the chunk to the buffer */
|
||||
g_music_buffer->Return(shifted);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
audio_output_all_wait(PlayerControl &pc, unsigned threshold)
|
||||
{
|
||||
pc.Lock();
|
||||
|
||||
if (audio_output_all_check() < threshold) {
|
||||
pc.Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
pc.Wait();
|
||||
pc.Unlock();
|
||||
|
||||
return audio_output_all_check() < threshold;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_pause(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
audio_output_all_update();
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_pause(audio_outputs[i]);
|
||||
|
||||
audio_output_wait_all();
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_drain(void)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_drain_async(audio_outputs[i]);
|
||||
|
||||
audio_output_wait_all();
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_cancel(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* send the cancel() command to all audio outputs */
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_cancel(audio_outputs[i]);
|
||||
|
||||
audio_output_wait_all();
|
||||
|
||||
/* clear the music pipe and return all chunks to the buffer */
|
||||
|
||||
if (g_mp != nullptr)
|
||||
g_mp->Clear(*g_music_buffer);
|
||||
|
||||
/* the audio outputs are now waiting for a signal, to
|
||||
synchronize the cleared music pipe */
|
||||
|
||||
audio_output_allow_play_all();
|
||||
|
||||
/* invalidate elapsed_time */
|
||||
|
||||
audio_output_all_elapsed_time = -1.0;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_close(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_close(audio_outputs[i]);
|
||||
|
||||
if (g_mp != nullptr) {
|
||||
assert(g_music_buffer != nullptr);
|
||||
|
||||
g_mp->Clear(*g_music_buffer);
|
||||
delete g_mp;
|
||||
g_mp = nullptr;
|
||||
}
|
||||
|
||||
g_music_buffer = nullptr;
|
||||
|
||||
input_audio_format.Clear();
|
||||
|
||||
audio_output_all_elapsed_time = -1.0;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_release(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_release(audio_outputs[i]);
|
||||
|
||||
if (g_mp != nullptr) {
|
||||
assert(g_music_buffer != nullptr);
|
||||
|
||||
g_mp->Clear(*g_music_buffer);
|
||||
delete g_mp;
|
||||
g_mp = nullptr;
|
||||
}
|
||||
|
||||
g_music_buffer = nullptr;
|
||||
|
||||
input_audio_format.Clear();
|
||||
|
||||
audio_output_all_elapsed_time = -1.0;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_all_song_border(void)
|
||||
{
|
||||
/* clear the elapsed_time pointer at the beginning of a new
|
||||
song */
|
||||
audio_output_all_elapsed_time = 0.0;
|
||||
}
|
||||
|
||||
float
|
||||
audio_output_all_get_elapsed_time(void)
|
||||
{
|
||||
return audio_output_all_elapsed_time;
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Functions for dealing with all configured (enabled) audion outputs
|
||||
* at once.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OUTPUT_ALL_H
|
||||
#define OUTPUT_ALL_H
|
||||
|
||||
#include "ReplayGainInfo.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
struct AudioFormat;
|
||||
class MusicBuffer;
|
||||
struct music_chunk;
|
||||
struct PlayerControl;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Global initialization: load audio outputs from the configuration
|
||||
* file and initialize them.
|
||||
*/
|
||||
void
|
||||
audio_output_all_init(PlayerControl &pc);
|
||||
|
||||
/**
|
||||
* Global finalization: free memory occupied by audio outputs. All
|
||||
*/
|
||||
void
|
||||
audio_output_all_finish(void);
|
||||
|
||||
/**
|
||||
* Returns the total number of audio output devices, including those
|
||||
* who are disabled right now.
|
||||
*/
|
||||
gcc_const
|
||||
unsigned int audio_output_count(void);
|
||||
|
||||
/**
|
||||
* Returns the "i"th audio output device.
|
||||
*/
|
||||
gcc_const
|
||||
struct audio_output *
|
||||
audio_output_get(unsigned i);
|
||||
|
||||
/**
|
||||
* Returns the audio output device with the specified name. Returns
|
||||
* NULL if the name does not exist.
|
||||
*/
|
||||
gcc_pure
|
||||
struct audio_output *
|
||||
audio_output_find(const char *name);
|
||||
|
||||
/**
|
||||
* Checks the "enabled" flag of all audio outputs, and if one has
|
||||
* changed, commit the change.
|
||||
*/
|
||||
void
|
||||
audio_output_all_enable_disable(void);
|
||||
|
||||
/**
|
||||
* Opens all audio outputs which are not disabled.
|
||||
*
|
||||
* @param audio_format the preferred audio format
|
||||
* @param buffer the #music_buffer where consumed #music_chunk objects
|
||||
* should be returned
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool
|
||||
audio_output_all_open(AudioFormat audio_format,
|
||||
MusicBuffer &buffer,
|
||||
Error &error);
|
||||
|
||||
/**
|
||||
* Closes all audio outputs.
|
||||
*/
|
||||
void
|
||||
audio_output_all_close(void);
|
||||
|
||||
/**
|
||||
* Closes all audio outputs. Outputs with the "always_on" flag are
|
||||
* put into pause mode.
|
||||
*/
|
||||
void
|
||||
audio_output_all_release(void);
|
||||
|
||||
void
|
||||
audio_output_all_set_replay_gain_mode(ReplayGainMode mode);
|
||||
|
||||
/**
|
||||
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
|
||||
* #MusicPipe.
|
||||
*
|
||||
* @param chunk the #music_chunk object to be played
|
||||
* @return true on success, false if no audio output was able to play
|
||||
* (all closed then)
|
||||
*/
|
||||
bool
|
||||
audio_output_all_play(struct music_chunk *chunk, Error &error);
|
||||
|
||||
/**
|
||||
* Checks if the output devices have drained their music pipe, and
|
||||
* returns the consumed music chunks to the #music_buffer.
|
||||
*
|
||||
* @return the number of chunks to play left in the #MusicPipe
|
||||
*/
|
||||
unsigned
|
||||
audio_output_all_check(void);
|
||||
|
||||
/**
|
||||
* Checks if the size of the #MusicPipe is below the #threshold. If
|
||||
* not, it attempts to synchronize with all output threads, and waits
|
||||
* until another #music_chunk is finished.
|
||||
*
|
||||
* @param threshold the maximum number of chunks in the pipe
|
||||
* @return true if there are less than #threshold chunks in the pipe
|
||||
*/
|
||||
bool
|
||||
audio_output_all_wait(PlayerControl &pc, unsigned threshold);
|
||||
|
||||
/**
|
||||
* Puts all audio outputs into pause mode. Most implementations will
|
||||
* simply close it then.
|
||||
*/
|
||||
void
|
||||
audio_output_all_pause(void);
|
||||
|
||||
/**
|
||||
* Drain all audio outputs.
|
||||
*/
|
||||
void
|
||||
audio_output_all_drain(void);
|
||||
|
||||
/**
|
||||
* Try to cancel data which may still be in the device's buffers.
|
||||
*/
|
||||
void
|
||||
audio_output_all_cancel(void);
|
||||
|
||||
/**
|
||||
* Indicate that a new song will begin now.
|
||||
*/
|
||||
void
|
||||
audio_output_all_song_border(void);
|
||||
|
||||
/**
|
||||
* Returns the "elapsed_time" stamp of the most recently finished
|
||||
* chunk. A negative value is returned when no chunk has been
|
||||
* finished yet.
|
||||
*/
|
||||
gcc_pure
|
||||
float
|
||||
audio_output_all_get_elapsed_time(void);
|
||||
|
||||
#endif
|
||||
@@ -1,336 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "OutputControl.hxx"
|
||||
#include "OutputThread.hxx"
|
||||
#include "OutputInternal.hxx"
|
||||
#include "OutputPlugin.hxx"
|
||||
#include "OutputError.hxx"
|
||||
#include "MixerPlugin.hxx"
|
||||
#include "MixerControl.hxx"
|
||||
#include "notify.hxx"
|
||||
#include "filter/ReplayGainFilterPlugin.hxx"
|
||||
#include "FilterPlugin.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/** after a failure, wait this number of seconds before
|
||||
automatically reopening the device */
|
||||
static constexpr unsigned REOPEN_AFTER = 10;
|
||||
|
||||
struct notify audio_output_client_notify;
|
||||
|
||||
/**
|
||||
* Waits for command completion.
|
||||
*
|
||||
* @param ao the #audio_output instance; must be locked
|
||||
*/
|
||||
static void ao_command_wait(struct audio_output *ao)
|
||||
{
|
||||
while (ao->command != AO_COMMAND_NONE) {
|
||||
ao->mutex.unlock();
|
||||
audio_output_client_notify.Wait();
|
||||
ao->mutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a command to the #audio_output object, but does not wait for
|
||||
* completion.
|
||||
*
|
||||
* @param ao the #audio_output instance; must be locked
|
||||
*/
|
||||
static void ao_command_async(struct audio_output *ao,
|
||||
enum audio_output_command cmd)
|
||||
{
|
||||
assert(ao->command == AO_COMMAND_NONE);
|
||||
ao->command = cmd;
|
||||
ao->cond.signal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a command to the #audio_output object and waits for
|
||||
* completion.
|
||||
*
|
||||
* @param ao the #audio_output instance; must be locked
|
||||
*/
|
||||
static void
|
||||
ao_command(struct audio_output *ao, enum audio_output_command cmd)
|
||||
{
|
||||
ao_command_async(ao, cmd);
|
||||
ao_command_wait(ao);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the #audio_output object and execute the command
|
||||
* synchronously.
|
||||
*/
|
||||
static void
|
||||
ao_lock_command(struct audio_output *ao, enum audio_output_command cmd)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
ao_command(ao, cmd);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_set_replay_gain_mode(struct audio_output *ao,
|
||||
ReplayGainMode mode)
|
||||
{
|
||||
if (ao->replay_gain_filter != nullptr)
|
||||
replay_gain_filter_set_mode(ao->replay_gain_filter, mode);
|
||||
if (ao->other_replay_gain_filter != nullptr)
|
||||
replay_gain_filter_set_mode(ao->other_replay_gain_filter, mode);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_enable(struct audio_output *ao)
|
||||
{
|
||||
if (!ao->thread.IsDefined()) {
|
||||
if (ao->plugin->enable == nullptr) {
|
||||
/* don't bother to start the thread now if the
|
||||
device doesn't even have a enable() method;
|
||||
just assign the variable and we're done */
|
||||
ao->really_enabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
audio_output_thread_start(ao);
|
||||
}
|
||||
|
||||
ao_lock_command(ao, AO_COMMAND_ENABLE);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_disable(struct audio_output *ao)
|
||||
{
|
||||
if (!ao->thread.IsDefined()) {
|
||||
if (ao->plugin->disable == nullptr)
|
||||
ao->really_enabled = false;
|
||||
else
|
||||
/* if there's no thread yet, the device cannot
|
||||
be enabled */
|
||||
assert(!ao->really_enabled);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ao_lock_command(ao, AO_COMMAND_DISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object must be locked (and unlocked) by the caller.
|
||||
*/
|
||||
static bool
|
||||
audio_output_open(struct audio_output *ao,
|
||||
const AudioFormat audio_format,
|
||||
const MusicPipe &mp)
|
||||
{
|
||||
bool open;
|
||||
|
||||
assert(ao != nullptr);
|
||||
assert(ao->allow_play);
|
||||
assert(audio_format.IsValid());
|
||||
|
||||
if (ao->fail_timer != nullptr) {
|
||||
g_timer_destroy(ao->fail_timer);
|
||||
ao->fail_timer = nullptr;
|
||||
}
|
||||
|
||||
if (ao->open && audio_format == ao->in_audio_format) {
|
||||
assert(ao->pipe == &mp ||
|
||||
(ao->always_on && ao->pause));
|
||||
|
||||
if (ao->pause) {
|
||||
ao->chunk = nullptr;
|
||||
ao->pipe = ∓
|
||||
|
||||
/* unpause with the CANCEL command; this is a
|
||||
hack, but suits well for forcing the thread
|
||||
to leave the ao_pause() thread, and we need
|
||||
to flush the device buffer anyway */
|
||||
|
||||
/* we're not using audio_output_cancel() here,
|
||||
because that function is asynchronous */
|
||||
ao_command(ao, AO_COMMAND_CANCEL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ao->in_audio_format = audio_format;
|
||||
ao->chunk = nullptr;
|
||||
|
||||
ao->pipe = ∓
|
||||
|
||||
if (!ao->thread.IsDefined())
|
||||
audio_output_thread_start(ao);
|
||||
|
||||
ao_command(ao, ao->open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN);
|
||||
open = ao->open;
|
||||
|
||||
if (open && ao->mixer != nullptr) {
|
||||
Error error;
|
||||
if (!mixer_open(ao->mixer, error))
|
||||
FormatWarning(output_domain,
|
||||
"Failed to open mixer for '%s'",
|
||||
ao->name);
|
||||
}
|
||||
|
||||
return open;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as audio_output_close(), but expects the lock to be held by
|
||||
* the caller.
|
||||
*/
|
||||
static void
|
||||
audio_output_close_locked(struct audio_output *ao)
|
||||
{
|
||||
assert(ao != nullptr);
|
||||
assert(ao->allow_play);
|
||||
|
||||
if (ao->mixer != nullptr)
|
||||
mixer_auto_close(ao->mixer);
|
||||
|
||||
assert(!ao->open || ao->fail_timer == nullptr);
|
||||
|
||||
if (ao->open)
|
||||
ao_command(ao, AO_COMMAND_CLOSE);
|
||||
else if (ao->fail_timer != nullptr) {
|
||||
g_timer_destroy(ao->fail_timer);
|
||||
ao->fail_timer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
audio_output_update(struct audio_output *ao,
|
||||
const AudioFormat audio_format,
|
||||
const MusicPipe &mp)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
if (ao->enabled && ao->really_enabled) {
|
||||
if (ao->fail_timer == nullptr ||
|
||||
g_timer_elapsed(ao->fail_timer, nullptr) > REOPEN_AFTER) {
|
||||
return audio_output_open(ao, audio_format, mp);
|
||||
}
|
||||
} else if (audio_output_is_open(ao))
|
||||
audio_output_close_locked(ao);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_play(struct audio_output *ao)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
|
||||
if (audio_output_is_open(ao) && !ao->in_playback_loop &&
|
||||
!ao->woken_for_play) {
|
||||
ao->woken_for_play = true;
|
||||
ao->cond.signal();
|
||||
}
|
||||
}
|
||||
|
||||
void audio_output_pause(struct audio_output *ao)
|
||||
{
|
||||
if (ao->mixer != nullptr && ao->plugin->pause == nullptr)
|
||||
/* the device has no pause mode: close the mixer,
|
||||
unless its "global" flag is set (checked by
|
||||
mixer_auto_close()) */
|
||||
mixer_auto_close(ao->mixer);
|
||||
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
if (audio_output_is_open(ao))
|
||||
ao_command_async(ao, AO_COMMAND_PAUSE);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_drain_async(struct audio_output *ao)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
if (audio_output_is_open(ao))
|
||||
ao_command_async(ao, AO_COMMAND_DRAIN);
|
||||
}
|
||||
|
||||
void audio_output_cancel(struct audio_output *ao)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
if (audio_output_is_open(ao)) {
|
||||
ao->allow_play = false;
|
||||
ao_command_async(ao, AO_COMMAND_CANCEL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_allow_play(struct audio_output *ao)
|
||||
{
|
||||
const ScopeLock protect(ao->mutex);
|
||||
|
||||
ao->allow_play = true;
|
||||
if (audio_output_is_open(ao))
|
||||
ao->cond.signal();
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_release(struct audio_output *ao)
|
||||
{
|
||||
if (ao->always_on)
|
||||
audio_output_pause(ao);
|
||||
else
|
||||
audio_output_close(ao);
|
||||
}
|
||||
|
||||
void audio_output_close(struct audio_output *ao)
|
||||
{
|
||||
assert(ao != nullptr);
|
||||
assert(!ao->open || ao->fail_timer == nullptr);
|
||||
|
||||
const ScopeLock protect(ao->mutex);
|
||||
audio_output_close_locked(ao);
|
||||
}
|
||||
|
||||
void audio_output_finish(struct audio_output *ao)
|
||||
{
|
||||
audio_output_close(ao);
|
||||
|
||||
assert(ao->fail_timer == nullptr);
|
||||
|
||||
if (ao->thread.IsDefined()) {
|
||||
assert(ao->allow_play);
|
||||
ao_lock_command(ao, AO_COMMAND_KILL);
|
||||
ao->thread.Join();
|
||||
}
|
||||
|
||||
audio_output_free(ao);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_OUTPUT_CONTROL_HXX
|
||||
#define MPD_OUTPUT_CONTROL_HXX
|
||||
|
||||
#include "ReplayGainInfo.hxx"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct audio_output;
|
||||
struct AudioFormat;
|
||||
struct config_param;
|
||||
class MusicPipe;
|
||||
|
||||
void
|
||||
audio_output_set_replay_gain_mode(struct audio_output *ao,
|
||||
ReplayGainMode mode);
|
||||
|
||||
/**
|
||||
* Enables the device.
|
||||
*/
|
||||
void
|
||||
audio_output_enable(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Disables the device.
|
||||
*/
|
||||
void
|
||||
audio_output_disable(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Opens or closes the device, depending on the "enabled" flag.
|
||||
*
|
||||
* @return true if the device is open
|
||||
*/
|
||||
bool
|
||||
audio_output_update(struct audio_output *ao,
|
||||
AudioFormat audio_format,
|
||||
const MusicPipe &mp);
|
||||
|
||||
void
|
||||
audio_output_play(struct audio_output *ao);
|
||||
|
||||
void audio_output_pause(struct audio_output *ao);
|
||||
|
||||
void
|
||||
audio_output_drain_async(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Clear the "allow_play" flag and send the "CANCEL" command
|
||||
* asynchronously. To finish the operation, the caller has to call
|
||||
* audio_output_allow_play().
|
||||
*/
|
||||
void audio_output_cancel(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Set the "allow_play" and signal the thread.
|
||||
*/
|
||||
void
|
||||
audio_output_allow_play(struct audio_output *ao);
|
||||
|
||||
void audio_output_close(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Closes the audio output, but if the "always_on" flag is set, put it
|
||||
* into pause mode instead.
|
||||
*/
|
||||
void
|
||||
audio_output_release(struct audio_output *ao);
|
||||
|
||||
void audio_output_finish(struct audio_output *ao);
|
||||
|
||||
#endif
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "OutputPlugin.hxx"
|
||||
#include "OutputInternal.hxx"
|
||||
|
||||
struct audio_output *
|
||||
ao_plugin_init(const struct audio_output_plugin *plugin,
|
||||
const config_param ¶m,
|
||||
Error &error)
|
||||
{
|
||||
assert(plugin != nullptr);
|
||||
assert(plugin->init != nullptr);
|
||||
|
||||
return plugin->init(param, error);
|
||||
}
|
||||
|
||||
void
|
||||
ao_plugin_finish(struct audio_output *ao)
|
||||
{
|
||||
ao->plugin->finish(ao);
|
||||
}
|
||||
|
||||
bool
|
||||
ao_plugin_enable(struct audio_output *ao, Error &error_r)
|
||||
{
|
||||
return ao->plugin->enable != nullptr
|
||||
? ao->plugin->enable(ao, error_r)
|
||||
: true;
|
||||
}
|
||||
|
||||
void
|
||||
ao_plugin_disable(struct audio_output *ao)
|
||||
{
|
||||
if (ao->plugin->disable != nullptr)
|
||||
ao->plugin->disable(ao);
|
||||
}
|
||||
|
||||
bool
|
||||
ao_plugin_open(struct audio_output *ao, AudioFormat &audio_format,
|
||||
Error &error)
|
||||
{
|
||||
return ao->plugin->open(ao, audio_format, error);
|
||||
}
|
||||
|
||||
void
|
||||
ao_plugin_close(struct audio_output *ao)
|
||||
{
|
||||
ao->plugin->close(ao);
|
||||
}
|
||||
|
||||
unsigned
|
||||
ao_plugin_delay(struct audio_output *ao)
|
||||
{
|
||||
return ao->plugin->delay != nullptr
|
||||
? ao->plugin->delay(ao)
|
||||
: 0;
|
||||
}
|
||||
|
||||
void
|
||||
ao_plugin_send_tag(struct audio_output *ao, const Tag *tag)
|
||||
{
|
||||
if (ao->plugin->send_tag != nullptr)
|
||||
ao->plugin->send_tag(ao, tag);
|
||||
}
|
||||
|
||||
size_t
|
||||
ao_plugin_play(struct audio_output *ao, const void *chunk, size_t size,
|
||||
Error &error)
|
||||
{
|
||||
return ao->plugin->play(ao, chunk, size, error);
|
||||
}
|
||||
|
||||
void
|
||||
ao_plugin_drain(struct audio_output *ao)
|
||||
{
|
||||
if (ao->plugin->drain != nullptr)
|
||||
ao->plugin->drain(ao);
|
||||
}
|
||||
|
||||
void
|
||||
ao_plugin_cancel(struct audio_output *ao)
|
||||
{
|
||||
if (ao->plugin->cancel != nullptr)
|
||||
ao->plugin->cancel(ao);
|
||||
}
|
||||
|
||||
bool
|
||||
ao_plugin_pause(struct audio_output *ao)
|
||||
{
|
||||
return ao->plugin->pause != nullptr && ao->plugin->pause(ao);
|
||||
}
|
||||
@@ -1,679 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "OutputThread.hxx"
|
||||
#include "OutputInternal.hxx"
|
||||
#include "OutputAPI.hxx"
|
||||
#include "OutputError.hxx"
|
||||
#include "pcm/PcmMix.hxx"
|
||||
#include "notify.hxx"
|
||||
#include "FilterInternal.hxx"
|
||||
#include "filter/ConvertFilterPlugin.hxx"
|
||||
#include "filter/ReplayGainFilterPlugin.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "MusicPipe.hxx"
|
||||
#include "MusicChunk.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void ao_command_finished(struct audio_output *ao)
|
||||
{
|
||||
assert(ao->command != AO_COMMAND_NONE);
|
||||
ao->command = AO_COMMAND_NONE;
|
||||
|
||||
ao->mutex.unlock();
|
||||
audio_output_client_notify.Signal();
|
||||
ao->mutex.lock();
|
||||
}
|
||||
|
||||
static bool
|
||||
ao_enable(struct audio_output *ao)
|
||||
{
|
||||
Error error;
|
||||
bool success;
|
||||
|
||||
if (ao->really_enabled)
|
||||
return true;
|
||||
|
||||
ao->mutex.unlock();
|
||||
success = ao_plugin_enable(ao, error);
|
||||
ao->mutex.lock();
|
||||
if (!success) {
|
||||
FormatError(error,
|
||||
"Failed to enable \"%s\" [%s]",
|
||||
ao->name, ao->plugin->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
ao->really_enabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ao_close(struct audio_output *ao, bool drain);
|
||||
|
||||
static void
|
||||
ao_disable(struct audio_output *ao)
|
||||
{
|
||||
if (ao->open)
|
||||
ao_close(ao, false);
|
||||
|
||||
if (ao->really_enabled) {
|
||||
ao->really_enabled = false;
|
||||
|
||||
ao->mutex.unlock();
|
||||
ao_plugin_disable(ao);
|
||||
ao->mutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
static AudioFormat
|
||||
ao_filter_open(struct audio_output *ao, AudioFormat &format,
|
||||
Error &error_r)
|
||||
{
|
||||
assert(format.IsValid());
|
||||
|
||||
/* the replay_gain filter cannot fail here */
|
||||
if (ao->replay_gain_filter != nullptr)
|
||||
ao->replay_gain_filter->Open(format, error_r);
|
||||
if (ao->other_replay_gain_filter != nullptr)
|
||||
ao->other_replay_gain_filter->Open(format, error_r);
|
||||
|
||||
const AudioFormat af = ao->filter->Open(format, error_r);
|
||||
if (!af.IsDefined()) {
|
||||
if (ao->replay_gain_filter != nullptr)
|
||||
ao->replay_gain_filter->Close();
|
||||
if (ao->other_replay_gain_filter != nullptr)
|
||||
ao->other_replay_gain_filter->Close();
|
||||
}
|
||||
|
||||
return af;
|
||||
}
|
||||
|
||||
static void
|
||||
ao_filter_close(struct audio_output *ao)
|
||||
{
|
||||
if (ao->replay_gain_filter != nullptr)
|
||||
ao->replay_gain_filter->Close();
|
||||
if (ao->other_replay_gain_filter != nullptr)
|
||||
ao->other_replay_gain_filter->Close();
|
||||
|
||||
ao->filter->Close();
|
||||
}
|
||||
|
||||
static void
|
||||
ao_open(struct audio_output *ao)
|
||||
{
|
||||
bool success;
|
||||
Error error;
|
||||
struct audio_format_string af_string;
|
||||
|
||||
assert(!ao->open);
|
||||
assert(ao->pipe != nullptr);
|
||||
assert(ao->chunk == nullptr);
|
||||
assert(ao->in_audio_format.IsValid());
|
||||
|
||||
if (ao->fail_timer != nullptr) {
|
||||
/* this can only happen when this
|
||||
output thread fails while
|
||||
audio_output_open() is run in the
|
||||
player thread */
|
||||
g_timer_destroy(ao->fail_timer);
|
||||
ao->fail_timer = nullptr;
|
||||
}
|
||||
|
||||
/* enable the device (just in case the last enable has failed) */
|
||||
|
||||
if (!ao_enable(ao))
|
||||
/* still no luck */
|
||||
return;
|
||||
|
||||
/* open the filter */
|
||||
|
||||
const AudioFormat filter_audio_format =
|
||||
ao_filter_open(ao, ao->in_audio_format, error);
|
||||
if (!filter_audio_format.IsDefined()) {
|
||||
FormatError(error, "Failed to open filter for \"%s\" [%s]",
|
||||
ao->name, ao->plugin->name);
|
||||
|
||||
ao->fail_timer = g_timer_new();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(filter_audio_format.IsValid());
|
||||
|
||||
ao->out_audio_format = filter_audio_format;
|
||||
ao->out_audio_format.ApplyMask(ao->config_audio_format);
|
||||
|
||||
ao->mutex.unlock();
|
||||
success = ao_plugin_open(ao, ao->out_audio_format, error);
|
||||
ao->mutex.lock();
|
||||
|
||||
assert(!ao->open);
|
||||
|
||||
if (!success) {
|
||||
FormatError(error, "Failed to open \"%s\" [%s]",
|
||||
ao->name, ao->plugin->name);
|
||||
|
||||
ao_filter_close(ao);
|
||||
ao->fail_timer = g_timer_new();
|
||||
return;
|
||||
}
|
||||
|
||||
convert_filter_set(ao->convert_filter, ao->out_audio_format);
|
||||
|
||||
ao->open = true;
|
||||
|
||||
FormatDebug(output_domain,
|
||||
"opened plugin=%s name=\"%s\" audio_format=%s",
|
||||
ao->plugin->name, ao->name,
|
||||
audio_format_to_string(ao->out_audio_format, &af_string));
|
||||
|
||||
if (ao->in_audio_format != ao->out_audio_format)
|
||||
FormatDebug(output_domain, "converting from %s",
|
||||
audio_format_to_string(ao->in_audio_format,
|
||||
&af_string));
|
||||
}
|
||||
|
||||
static void
|
||||
ao_close(struct audio_output *ao, bool drain)
|
||||
{
|
||||
assert(ao->open);
|
||||
|
||||
ao->pipe = nullptr;
|
||||
|
||||
ao->chunk = nullptr;
|
||||
ao->open = false;
|
||||
|
||||
ao->mutex.unlock();
|
||||
|
||||
if (drain)
|
||||
ao_plugin_drain(ao);
|
||||
else
|
||||
ao_plugin_cancel(ao);
|
||||
|
||||
ao_plugin_close(ao);
|
||||
ao_filter_close(ao);
|
||||
|
||||
ao->mutex.lock();
|
||||
|
||||
FormatDebug(output_domain, "closed plugin=%s name=\"%s\"",
|
||||
ao->plugin->name, ao->name);
|
||||
}
|
||||
|
||||
static void
|
||||
ao_reopen_filter(struct audio_output *ao)
|
||||
{
|
||||
Error error;
|
||||
|
||||
ao_filter_close(ao);
|
||||
const AudioFormat filter_audio_format =
|
||||
ao_filter_open(ao, ao->in_audio_format, error);
|
||||
if (!filter_audio_format.IsDefined()) {
|
||||
FormatError(error,
|
||||
"Failed to open filter for \"%s\" [%s]",
|
||||
ao->name, ao->plugin->name);
|
||||
|
||||
/* this is a little code duplication fro ao_close(),
|
||||
but we cannot call this function because we must
|
||||
not call filter_close(ao->filter) again */
|
||||
|
||||
ao->pipe = nullptr;
|
||||
|
||||
ao->chunk = nullptr;
|
||||
ao->open = false;
|
||||
ao->fail_timer = g_timer_new();
|
||||
|
||||
ao->mutex.unlock();
|
||||
ao_plugin_close(ao);
|
||||
ao->mutex.lock();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
convert_filter_set(ao->convert_filter, ao->out_audio_format);
|
||||
}
|
||||
|
||||
static void
|
||||
ao_reopen(struct audio_output *ao)
|
||||
{
|
||||
if (!ao->config_audio_format.IsFullyDefined()) {
|
||||
if (ao->open) {
|
||||
const MusicPipe *mp = ao->pipe;
|
||||
ao_close(ao, true);
|
||||
ao->pipe = mp;
|
||||
}
|
||||
|
||||
/* no audio format is configured: copy in->out, let
|
||||
the output's open() method determine the effective
|
||||
out_audio_format */
|
||||
ao->out_audio_format = ao->in_audio_format;
|
||||
ao->out_audio_format.ApplyMask(ao->config_audio_format);
|
||||
}
|
||||
|
||||
if (ao->open)
|
||||
/* the audio format has changed, and all filters have
|
||||
to be reconfigured */
|
||||
ao_reopen_filter(ao);
|
||||
else
|
||||
ao_open(ao);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the output's delay reaches zero.
|
||||
*
|
||||
* @return true if playback should be continued, false if a command
|
||||
* was issued
|
||||
*/
|
||||
static bool
|
||||
ao_wait(struct audio_output *ao)
|
||||
{
|
||||
while (true) {
|
||||
unsigned delay = ao_plugin_delay(ao);
|
||||
if (delay == 0)
|
||||
return true;
|
||||
|
||||
(void)ao->cond.timed_wait(ao->mutex, delay);
|
||||
|
||||
if (ao->command != AO_COMMAND_NONE)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const void *
|
||||
ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
|
||||
Filter *replay_gain_filter,
|
||||
unsigned *replay_gain_serial_p,
|
||||
size_t *length_r)
|
||||
{
|
||||
assert(chunk != nullptr);
|
||||
assert(!chunk->IsEmpty());
|
||||
assert(chunk->CheckFormat(ao->in_audio_format));
|
||||
|
||||
const void *data = chunk->data;
|
||||
size_t length = chunk->length;
|
||||
|
||||
(void)ao;
|
||||
|
||||
assert(length % ao->in_audio_format.GetFrameSize() == 0);
|
||||
|
||||
if (length > 0 && replay_gain_filter != nullptr) {
|
||||
if (chunk->replay_gain_serial != *replay_gain_serial_p) {
|
||||
replay_gain_filter_set_info(replay_gain_filter,
|
||||
chunk->replay_gain_serial != 0
|
||||
? &chunk->replay_gain_info
|
||||
: nullptr);
|
||||
*replay_gain_serial_p = chunk->replay_gain_serial;
|
||||
}
|
||||
|
||||
Error error;
|
||||
data = replay_gain_filter->FilterPCM(data, length,
|
||||
&length, error);
|
||||
if (data == nullptr) {
|
||||
FormatError(error, "\"%s\" [%s] failed to filter",
|
||||
ao->name, ao->plugin->name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
*length_r = length;
|
||||
return data;
|
||||
}
|
||||
|
||||
static const void *
|
||||
ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
|
||||
size_t *length_r)
|
||||
{
|
||||
size_t length;
|
||||
const void *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
|
||||
&ao->replay_gain_serial, &length);
|
||||
if (data == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (length == 0) {
|
||||
/* empty chunk, nothing to do */
|
||||
*length_r = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* cross-fade */
|
||||
|
||||
if (chunk->other != nullptr) {
|
||||
size_t other_length;
|
||||
const void *other_data =
|
||||
ao_chunk_data(ao, chunk->other,
|
||||
ao->other_replay_gain_filter,
|
||||
&ao->other_replay_gain_serial,
|
||||
&other_length);
|
||||
if (other_data == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (other_length == 0) {
|
||||
*length_r = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* if the "other" chunk is longer, then that trailer
|
||||
is used as-is, without mixing; it is part of the
|
||||
"next" song being faded in, and if there's a rest,
|
||||
it means cross-fading ends here */
|
||||
|
||||
if (length > other_length)
|
||||
length = other_length;
|
||||
|
||||
void *dest = ao->cross_fade_buffer.Get(other_length);
|
||||
memcpy(dest, other_data, other_length);
|
||||
if (!pcm_mix(dest, data, length,
|
||||
ao->in_audio_format.format,
|
||||
1.0 - chunk->mix_ratio)) {
|
||||
FormatError(output_domain,
|
||||
"Cannot cross-fade format %s",
|
||||
sample_format_to_string(ao->in_audio_format.format));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data = dest;
|
||||
length = other_length;
|
||||
}
|
||||
|
||||
/* apply filter chain */
|
||||
|
||||
Error error;
|
||||
data = ao->filter->FilterPCM(data, length, &length, error);
|
||||
if (data == nullptr) {
|
||||
FormatError(error, "\"%s\" [%s] failed to filter",
|
||||
ao->name, ao->plugin->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*length_r = length;
|
||||
return data;
|
||||
}
|
||||
|
||||
static bool
|
||||
ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
|
||||
{
|
||||
assert(ao != nullptr);
|
||||
assert(ao->filter != nullptr);
|
||||
|
||||
if (ao->tags && gcc_unlikely(chunk->tag != nullptr)) {
|
||||
ao->mutex.unlock();
|
||||
ao_plugin_send_tag(ao, chunk->tag);
|
||||
ao->mutex.lock();
|
||||
}
|
||||
|
||||
size_t size;
|
||||
#if GCC_CHECK_VERSION(4,7)
|
||||
/* workaround -Wmaybe-uninitialized false positive */
|
||||
size = 0;
|
||||
#endif
|
||||
const char *data = (const char *)ao_filter_chunk(ao, chunk, &size);
|
||||
if (data == nullptr) {
|
||||
ao_close(ao, false);
|
||||
|
||||
/* don't automatically reopen this device for 10
|
||||
seconds */
|
||||
ao->fail_timer = g_timer_new();
|
||||
return false;
|
||||
}
|
||||
|
||||
Error error;
|
||||
|
||||
while (size > 0 && ao->command == AO_COMMAND_NONE) {
|
||||
size_t nbytes;
|
||||
|
||||
if (!ao_wait(ao))
|
||||
break;
|
||||
|
||||
ao->mutex.unlock();
|
||||
nbytes = ao_plugin_play(ao, data, size, error);
|
||||
ao->mutex.lock();
|
||||
if (nbytes == 0) {
|
||||
/* play()==0 means failure */
|
||||
FormatError(error, "\"%s\" [%s] failed to play",
|
||||
ao->name, ao->plugin->name);
|
||||
|
||||
ao_close(ao, false);
|
||||
|
||||
/* don't automatically reopen this device for
|
||||
10 seconds */
|
||||
assert(ao->fail_timer == nullptr);
|
||||
ao->fail_timer = g_timer_new();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(nbytes <= size);
|
||||
assert(nbytes % ao->out_audio_format.GetFrameSize() == 0);
|
||||
|
||||
data += nbytes;
|
||||
size -= nbytes;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct music_chunk *
|
||||
ao_next_chunk(struct audio_output *ao)
|
||||
{
|
||||
return ao->chunk != nullptr
|
||||
/* continue the previous play() call */
|
||||
? ao->chunk->next
|
||||
/* get the first chunk from the pipe */
|
||||
: ao->pipe->Peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays all remaining chunks, until the tail of the pipe has been
|
||||
* reached (and no more chunks are queued), or until a command is
|
||||
* received.
|
||||
*
|
||||
* @return true if at least one chunk has been available, false if the
|
||||
* tail of the pipe was already reached
|
||||
*/
|
||||
static bool
|
||||
ao_play(struct audio_output *ao)
|
||||
{
|
||||
bool success;
|
||||
const struct music_chunk *chunk;
|
||||
|
||||
assert(ao->pipe != nullptr);
|
||||
|
||||
chunk = ao_next_chunk(ao);
|
||||
if (chunk == nullptr)
|
||||
/* no chunk available */
|
||||
return false;
|
||||
|
||||
ao->chunk_finished = false;
|
||||
|
||||
assert(!ao->in_playback_loop);
|
||||
ao->in_playback_loop = true;
|
||||
|
||||
while (chunk != nullptr && ao->command == AO_COMMAND_NONE) {
|
||||
assert(!ao->chunk_finished);
|
||||
|
||||
ao->chunk = chunk;
|
||||
|
||||
success = ao_play_chunk(ao, chunk);
|
||||
if (!success) {
|
||||
assert(ao->chunk == nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
assert(ao->chunk == chunk);
|
||||
chunk = chunk->next;
|
||||
}
|
||||
|
||||
assert(ao->in_playback_loop);
|
||||
ao->in_playback_loop = false;
|
||||
|
||||
ao->chunk_finished = true;
|
||||
|
||||
ao->mutex.unlock();
|
||||
ao->player_control->LockSignal();
|
||||
ao->mutex.lock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ao_pause(struct audio_output *ao)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
ao->mutex.unlock();
|
||||
ao_plugin_cancel(ao);
|
||||
ao->mutex.lock();
|
||||
|
||||
ao->pause = true;
|
||||
ao_command_finished(ao);
|
||||
|
||||
do {
|
||||
if (!ao_wait(ao))
|
||||
break;
|
||||
|
||||
ao->mutex.unlock();
|
||||
ret = ao_plugin_pause(ao);
|
||||
ao->mutex.lock();
|
||||
|
||||
if (!ret) {
|
||||
ao_close(ao, false);
|
||||
break;
|
||||
}
|
||||
} while (ao->command == AO_COMMAND_NONE);
|
||||
|
||||
ao->pause = false;
|
||||
}
|
||||
|
||||
static void
|
||||
audio_output_task(void *arg)
|
||||
{
|
||||
struct audio_output *ao = (struct audio_output *)arg;
|
||||
|
||||
ao->mutex.lock();
|
||||
|
||||
while (1) {
|
||||
switch (ao->command) {
|
||||
case AO_COMMAND_NONE:
|
||||
break;
|
||||
|
||||
case AO_COMMAND_ENABLE:
|
||||
ao_enable(ao);
|
||||
ao_command_finished(ao);
|
||||
break;
|
||||
|
||||
case AO_COMMAND_DISABLE:
|
||||
ao_disable(ao);
|
||||
ao_command_finished(ao);
|
||||
break;
|
||||
|
||||
case AO_COMMAND_OPEN:
|
||||
ao_open(ao);
|
||||
ao_command_finished(ao);
|
||||
break;
|
||||
|
||||
case AO_COMMAND_REOPEN:
|
||||
ao_reopen(ao);
|
||||
ao_command_finished(ao);
|
||||
break;
|
||||
|
||||
case AO_COMMAND_CLOSE:
|
||||
assert(ao->open);
|
||||
assert(ao->pipe != nullptr);
|
||||
|
||||
ao_close(ao, false);
|
||||
ao_command_finished(ao);
|
||||
break;
|
||||
|
||||
case AO_COMMAND_PAUSE:
|
||||
if (!ao->open) {
|
||||
/* the output has failed after
|
||||
audio_output_all_pause() has
|
||||
submitted the PAUSE command; bail
|
||||
out */
|
||||
ao_command_finished(ao);
|
||||
break;
|
||||
}
|
||||
|
||||
ao_pause(ao);
|
||||
/* don't "break" here: this might cause
|
||||
ao_play() to be called when command==CLOSE
|
||||
ends the paused state - "continue" checks
|
||||
the new command first */
|
||||
continue;
|
||||
|
||||
case AO_COMMAND_DRAIN:
|
||||
if (ao->open) {
|
||||
assert(ao->chunk == nullptr);
|
||||
assert(ao->pipe->Peek() == nullptr);
|
||||
|
||||
ao->mutex.unlock();
|
||||
ao_plugin_drain(ao);
|
||||
ao->mutex.lock();
|
||||
}
|
||||
|
||||
ao_command_finished(ao);
|
||||
continue;
|
||||
|
||||
case AO_COMMAND_CANCEL:
|
||||
ao->chunk = nullptr;
|
||||
|
||||
if (ao->open) {
|
||||
ao->mutex.unlock();
|
||||
ao_plugin_cancel(ao);
|
||||
ao->mutex.lock();
|
||||
}
|
||||
|
||||
ao_command_finished(ao);
|
||||
continue;
|
||||
|
||||
case AO_COMMAND_KILL:
|
||||
ao->chunk = nullptr;
|
||||
ao_command_finished(ao);
|
||||
ao->mutex.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ao->open && ao->allow_play && ao_play(ao))
|
||||
/* don't wait for an event if there are more
|
||||
chunks in the pipe */
|
||||
continue;
|
||||
|
||||
if (ao->command == AO_COMMAND_NONE) {
|
||||
ao->woken_for_play = false;
|
||||
ao->cond.wait(ao->mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_output_thread_start(struct audio_output *ao)
|
||||
{
|
||||
assert(ao->command == AO_COMMAND_NONE);
|
||||
|
||||
Error error;
|
||||
if (!ao->thread.Start(audio_output_task, ao, error))
|
||||
FatalError(error);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,21 +19,29 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "Partition.hxx"
|
||||
#include "Song.hxx"
|
||||
#include "DetachedSong.hxx"
|
||||
#include "output/MultipleOutputs.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "Idle.hxx"
|
||||
#include "GlobalEvents.hxx"
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
void
|
||||
Partition::DatabaseModified()
|
||||
Partition::DatabaseModified(const Database &db)
|
||||
{
|
||||
playlist.DatabaseModified();
|
||||
playlist.DatabaseModified(db);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
Partition::TagModified()
|
||||
{
|
||||
Song *song = pc.LockReadTaggedSong();
|
||||
DetachedSong *song = pc.LockReadTaggedSong();
|
||||
if (song != nullptr) {
|
||||
playlist.TagModified(std::move(*song));
|
||||
song->Free();
|
||||
delete song;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,3 +50,24 @@ Partition::SyncWithPlayer()
|
||||
{
|
||||
playlist.SyncWithPlayer(pc);
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnPlayerSync()
|
||||
{
|
||||
GlobalEvents::Emit(GlobalEvents::PLAYLIST);
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnPlayerTagModified()
|
||||
{
|
||||
GlobalEvents::Emit(GlobalEvents::TAG);
|
||||
}
|
||||
|
||||
void
|
||||
Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
|
||||
{
|
||||
InvalidateHardwareVolume();
|
||||
|
||||
/* notify clients */
|
||||
idle_add(IDLE_MIXER);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2013 The Music Player Daemon Project
|
||||
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -20,20 +20,29 @@
|
||||
#ifndef MPD_PARTITION_HXX
|
||||
#define MPD_PARTITION_HXX
|
||||
|
||||
#include "Playlist.hxx"
|
||||
#include "queue/Playlist.hxx"
|
||||
#include "output/MultipleOutputs.hxx"
|
||||
#include "mixer/Listener.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "PlayerListener.hxx"
|
||||
#include "Chrono.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
struct Instance;
|
||||
class MultipleOutputs;
|
||||
class SongLoader;
|
||||
|
||||
/**
|
||||
* A partition of the Music Player Daemon. It is a separate unit with
|
||||
* a playlist, a player, outputs etc.
|
||||
*/
|
||||
struct Partition {
|
||||
struct Partition final : private PlayerListener, private MixerListener {
|
||||
Instance &instance;
|
||||
|
||||
struct playlist playlist;
|
||||
|
||||
MultipleOutputs outputs;
|
||||
|
||||
PlayerControl pc;
|
||||
|
||||
Partition(Instance &_instance,
|
||||
@@ -41,21 +50,17 @@ struct Partition {
|
||||
unsigned buffer_chunks,
|
||||
unsigned buffered_before_play)
|
||||
:instance(_instance), playlist(max_length),
|
||||
pc(buffer_chunks, buffered_before_play) {
|
||||
}
|
||||
outputs(*this),
|
||||
pc(*this, outputs, buffer_chunks, buffered_before_play) {}
|
||||
|
||||
void ClearQueue() {
|
||||
playlist.Clear(pc);
|
||||
}
|
||||
|
||||
PlaylistResult AppendFile(const char *path_utf8,
|
||||
unsigned *added_id=nullptr) {
|
||||
return playlist.AppendFile(pc, path_utf8, added_id);
|
||||
}
|
||||
|
||||
PlaylistResult AppendURI(const char *uri_utf8,
|
||||
unsigned *added_id=nullptr) {
|
||||
return playlist.AppendURI(pc, uri_utf8, added_id);
|
||||
unsigned AppendURI(const SongLoader &loader,
|
||||
const char *uri_utf8,
|
||||
Error &error) {
|
||||
return playlist.AppendURI(pc, loader, uri_utf8, error);
|
||||
}
|
||||
|
||||
PlaylistResult DeletePosition(unsigned position) {
|
||||
@@ -76,10 +81,14 @@ struct Partition {
|
||||
return playlist.DeleteRange(pc, start, end);
|
||||
}
|
||||
|
||||
void DeleteSong(const Song &song) {
|
||||
playlist.DeleteSong(pc, song);
|
||||
#ifdef ENABLE_DATABASE
|
||||
|
||||
void DeleteSong(const char *uri) {
|
||||
playlist.DeleteSong(pc, uri);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Shuffle(unsigned start, unsigned end) {
|
||||
playlist.Shuffle(pc, start, end);
|
||||
}
|
||||
@@ -134,15 +143,15 @@ struct Partition {
|
||||
}
|
||||
|
||||
PlaylistResult SeekSongPosition(unsigned song_position,
|
||||
float seek_time) {
|
||||
SongTime seek_time) {
|
||||
return playlist.SeekSongPosition(pc, song_position, seek_time);
|
||||
}
|
||||
|
||||
PlaylistResult SeekSongId(unsigned song_id, float seek_time) {
|
||||
PlaylistResult SeekSongId(unsigned song_id, SongTime seek_time) {
|
||||
return playlist.SeekSongId(pc, song_id, seek_time);
|
||||
}
|
||||
|
||||
PlaylistResult SeekCurrent(float seek_time, bool relative) {
|
||||
PlaylistResult SeekCurrent(SignedSongTime seek_time, bool relative) {
|
||||
return playlist.SeekCurrent(pc, seek_time, relative);
|
||||
}
|
||||
|
||||
@@ -166,11 +175,13 @@ struct Partition {
|
||||
playlist.SetConsume(new_value);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
/**
|
||||
* The database has been modified. Propagate the change to
|
||||
* all subsystems.
|
||||
*/
|
||||
void DatabaseModified();
|
||||
void DatabaseModified(const Database &db);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A tag in the play queue has been modified by the player
|
||||
@@ -182,6 +193,14 @@ struct Partition {
|
||||
* Synchronize the player with the play queue.
|
||||
*/
|
||||
void SyncWithPlayer();
|
||||
|
||||
private:
|
||||
/* virtual methods from class PlayerListener */
|
||||
virtual void OnPlayerSync() override;
|
||||
virtual void OnPlayerTagModified() override;
|
||||
|
||||
/* virtual methods from class MixerListener */
|
||||
virtual void OnMixerVolumeChanged(Mixer &mixer, int volume) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user