Compare commits
798 commits
Author | SHA1 | Date | |
---|---|---|---|
|
bccd4970d9 | ||
|
542158415f | ||
|
d32f77d02f | ||
|
869bdd9a9b | ||
|
d32ccd3d1e | ||
|
93739d5a0d | ||
|
8ecad92dc5 | ||
|
08abd46f55 | ||
|
dcd6166e89 | ||
|
646531f8ae | ||
|
b5b752c884 | ||
|
d553e51277 | ||
|
a0a9f79592 | ||
|
47f50052be | ||
|
b02cfb91f8 | ||
|
6c6971a530 | ||
|
b5a999999a | ||
|
234fb63267 | ||
|
d220667deb | ||
|
972a7604bd | ||
|
16d6c1100b | ||
|
5ea0640c68 | ||
|
9951b7cb6a | ||
|
857ea20512 | ||
|
01ea0435d0 | ||
|
b8b52e4164 | ||
|
2fb59552a7 | ||
|
645169e45a | ||
|
c558d97a56 | ||
|
bcd762b279 | ||
|
d74af63da0 | ||
|
6e28ac6825 | ||
|
f2071f091d | ||
|
acd0bcd171 | ||
|
be91faadeb | ||
|
67fdd0741f | ||
|
43e4dc4955 | ||
|
7988d3be32 | ||
|
b693c946b8 | ||
|
6340b08308 | ||
|
78ec3d39eb | ||
|
10bfb204b5 | ||
|
23334dd452 | ||
|
23be3b7f03 | ||
|
cb9c4d9c01 | ||
|
c0d411f002 | ||
|
747bd51bb7 | ||
|
29658129d5 | ||
|
4ac4787cc1 | ||
|
a421955c4a | ||
|
88f2dfc338 | ||
|
b8d6ab5c8c | ||
|
794a9d2e7c | ||
|
a76c4c7426 | ||
|
b5403522e0 | ||
|
dfd95b0a37 | ||
|
0e06f5457c | ||
|
1d847eb96c | ||
|
04e40a8370 | ||
|
fabaa3f561 | ||
|
7fe95980d7 | ||
|
bc8538cdd4 | ||
|
dd55d4a9e1 | ||
|
1a0ea3b5d3 | ||
|
7839f661a1 | ||
|
30db270af3 | ||
|
ab7c0a0afd | ||
|
69533e12c7 | ||
|
048bc7c30d | ||
|
5753c48245 | ||
|
3b4313d844 | ||
|
a92973c6aa | ||
|
91c4ed0c98 | ||
|
9e889399ec | ||
|
47a749ace0 | ||
|
4232fb3949 | ||
|
f96cfea2d3 | ||
|
1cd431019c | ||
|
3f0c8d379a | ||
|
72608ecea0 | ||
|
ac26a28f59 | ||
|
a19469325b | ||
|
f4a382c67f | ||
|
25cd57a3cb | ||
|
40d9574015 | ||
|
8cfddb875c | ||
|
54db6c17cb | ||
|
104e959ea2 | ||
|
9a23ff9ff9 | ||
|
6b23590ac8 | ||
|
2282538c42 | ||
|
73951d6fe7 | ||
|
8c019c7302 | ||
|
f7b2c481e6 | ||
|
2038a140ed | ||
|
18d3f76152 | ||
|
2237863f08 | ||
|
ac93ff038e | ||
|
184e1f14c9 | ||
|
c24d914127 | ||
|
4b0f4a43fd | ||
|
b67b2acd6a | ||
|
90c5833fe9 | ||
|
8b3a165d9a | ||
|
cbaacd35b1 | ||
|
9b683a9c56 | ||
|
7ff653b48f | ||
|
31281197d4 | ||
|
7cb010e337 | ||
|
d97b1e788a | ||
|
2401536e5c | ||
|
a1bbfbe3ac | ||
|
2cc7ff9c4a | ||
|
54c9f30e8a | ||
|
a09907ccd9 | ||
|
5d73246926 | ||
|
532d22b9bb | ||
|
aee05d5503 | ||
|
d4dcf29dd4 | ||
|
90a62821f4 | ||
|
f889b34b1b | ||
|
2733ee95f3 | ||
|
2042034868 | ||
|
0a849fc1d9 | ||
|
b0fd359388 | ||
|
318cfd71ea | ||
|
9c1c6d7130 | ||
|
cf77521bb7 | ||
|
f8ee7e5e46 | ||
|
e64c279e45 | ||
|
0de555c2fd | ||
|
442dfb726b | ||
|
afeb614481 | ||
|
63a5239346 | ||
|
0570ebedea | ||
|
1d8803e6f2 | ||
|
46aac2fa91 | ||
|
ff5a1c6c2c | ||
|
d99eda15b5 | ||
|
d3f99f8987 | ||
|
aafe817a1f | ||
|
36a2d8efd5 | ||
|
17b8647052 | ||
|
a3385d1af9 | ||
|
9cc0b8be8d | ||
|
25d6f6ea7c | ||
|
d7681bda71 | ||
|
355169475d | ||
|
918aa59920 | ||
|
00d42d519e | ||
|
b41fd74f31 | ||
|
72bfe1c846 | ||
|
713241a94f | ||
|
d06ee01a17 | ||
|
04db82d0ff | ||
|
bb78d49f23 | ||
|
428ffb1531 | ||
|
c89cc1cff4 | ||
|
4ccd1811b0 | ||
|
45352f9261 | ||
|
68a146f6b8 | ||
|
ea7eff40e0 | ||
|
45cb841ec0 | ||
|
e3b48bdc86 | ||
|
462a509c85 | ||
|
c7a160a9e7 | ||
|
2f9d7fe66d | ||
|
cc12da5507 | ||
|
b889586708 | ||
|
a8902b4ee6 | ||
|
5e324927c5 | ||
|
05e830c681 | ||
|
8c97f075cf | ||
|
83ba062010 | ||
|
10f8d0c2e1 | ||
|
9fc7c99022 | ||
|
00d861efc0 | ||
|
bfa9660437 | ||
|
24888a669b | ||
|
b05f428cde | ||
|
d5ca037943 | ||
|
bf5e6d85a3 | ||
|
2be457f395 | ||
|
955b90280f | ||
|
1909b26671 | ||
|
fb5193ab62 | ||
|
6855449550 | ||
|
bde8f23ab1 | ||
|
d47a96acaa | ||
|
4a0cca22b8 | ||
|
30d9eeec2b | ||
|
9f6134a588 | ||
|
375b019972 | ||
|
fa34d469df | ||
|
7c169f9ef1 | ||
|
c02eabcf70 | ||
|
c45070138a | ||
|
c1665b6ee3 | ||
|
e3209e8901 | ||
|
ff9bd89389 | ||
|
e49ba8e3e0 | ||
|
169c798bef | ||
|
122757b42a | ||
|
0f1be5939d | ||
|
212ef0ffdf | ||
|
1c92c56d75 | ||
|
2e884b9e85 | ||
|
7e23e80b24 | ||
|
65e2da2d9e | ||
|
023d7e2ba7 | ||
|
44b8aa0dd8 | ||
|
f251c91f02 | ||
|
dd5be9b117 | ||
|
b8698ea125 | ||
|
f239784bf2 | ||
|
4face7d631 | ||
|
04426d755c | ||
|
931da40b4d | ||
|
0d289e47ab | ||
|
abf77a70d2 | ||
|
923179c4eb | ||
|
abd11ebc3b | ||
|
632e53510c | ||
|
cdddcaecd8 | ||
|
72a4a1c245 | ||
|
ab62de0fa2 | ||
|
75441bc5fa | ||
|
ae4c49ea68 | ||
|
146dae216a | ||
|
6470b2627f | ||
|
a40fcbca37 | ||
|
aba5b8d120 | ||
|
86ce3cf69b | ||
|
fd8b20db86 | ||
|
a0e68df0e2 | ||
|
5b8363c9ec | ||
|
d2284df683 | ||
|
3ed6e8a6b9 | ||
|
d9a08914df | ||
|
81b7dcfc0c | ||
|
9701bd5421 | ||
|
3cc97b5574 | ||
|
a78933ca80 | ||
|
8790df1ef0 | ||
|
9346a4edbd | ||
|
b39882168b | ||
|
d17388ee95 | ||
|
b913d423a2 | ||
|
7543020291 | ||
|
9fd393b64e | ||
|
a82742e689 | ||
|
2364a7d469 | ||
|
7994929a80 | ||
|
1e1ee2fbe2 | ||
|
b4c8c11218 | ||
|
26efcda71a | ||
|
dc0a44067a | ||
|
7179abe8ef | ||
|
724144accd | ||
|
0e608481d9 | ||
|
98bac743df | ||
|
cddfc3d1db | ||
|
cb8d873b5b | ||
|
9b3487b6e0 | ||
|
4890ee67a6 | ||
|
9e82e2737e | ||
|
7d803a0211 | ||
|
d6923781ef | ||
d8d3265016 | |||
|
7fc6361d6c | ||
|
266da8cc07 | ||
|
227ed0dfa3 | ||
48d89d4496 | |||
|
6f6773da09 | ||
|
ad074ec13e | ||
|
e4b70f53ae | ||
|
5392a5fa91 | ||
|
591f3ceb18 | ||
|
6e53cebf5c | ||
|
0b98c0ac98 | ||
|
6c24ad8244 | ||
|
170fb06a62 | ||
|
f4cfad370d | ||
|
dc81605343 | ||
|
994d0e328e | ||
|
ffa46ae2d0 | ||
|
cd95cd68f8 | ||
|
d835c252fa | ||
|
7777e50f03 | ||
|
b0414f4399 | ||
|
a873441484 | ||
|
dd4a0b7af3 | ||
cfa4e078ea | |||
|
b0368a7010 | ||
|
458d0a3824 | ||
|
838d5ca9f7 | ||
|
7e98a0398b | ||
|
e400fafa5d | ||
|
8bc2d3cd38 | ||
|
68c334d96e | ||
|
9f07f04ed6 | ||
|
0707c4bb71 | ||
|
75ef35efa6 | ||
|
d89c9c852f | ||
|
eb72b73e6c | ||
|
9c6915ecb2 | ||
|
59f3f416ba | ||
|
7e3112efc0 | ||
|
afe65df6c6 | ||
|
f5be598311 | ||
|
aa491fe0bb | ||
|
de43b1b226 | ||
|
db5b4e68c2 | ||
|
820a1dedfb | ||
|
ef8dd958ae | ||
|
eba9dc2c43 | ||
|
6a309a3075 | ||
|
2e5140a321 | ||
|
edbdc8d98e | ||
|
a3e6636731 | ||
|
389f56e392 | ||
|
de24bd4560 | ||
|
fcddcdce61 | ||
|
ba91d2e5ba | ||
|
b997a6d5dc | ||
|
63264de462 | ||
|
256c99badd | ||
|
d3415a3a25 | ||
|
03b52685bd | ||
|
4d11ba266e | ||
|
6dd01e07c1 | ||
|
b701a4d347 | ||
|
9f0ef52fd4 | ||
|
42ea824972 | ||
|
1c95771ed0 | ||
|
bc330e75f6 | ||
|
87684a7974 | ||
|
5e27f084e9 | ||
|
8997ffa030 | ||
|
2fc42ccb73 | ||
|
1eee2fbf11 | ||
|
6b2964328a | ||
|
1f497ee8bb | ||
|
73252a8928 | ||
|
b07a6fd098 | ||
|
2102fc1c44 | ||
|
09f7496d9a | ||
|
4ced731194 | ||
|
8c67b6c2f7 | ||
|
d88a90e5b0 | ||
|
122b6d6a6d | ||
|
fb72db74f8 | ||
|
2987857b93 | ||
|
df198110ed | ||
|
4ddca15a8f | ||
|
919661c9e8 | ||
|
cfd5b1c6fb | ||
|
a2e4fa17b2 | ||
|
e59c7be0b8 | ||
|
9e96bb2b75 | ||
|
7ab2a8305a | ||
|
5e36affad8 | ||
|
e85b5eea34 | ||
|
f3d218fd3d | ||
|
f54dd1c7b1 | ||
|
d4196a5595 | ||
|
09c9711b50 | ||
|
40b5bf7e13 | ||
|
fb208b4cd5 | ||
|
57158da628 | ||
|
a01d9ee5b3 | ||
|
9593902aff | ||
|
a3c8149612 | ||
|
1bffcaa429 | ||
|
d62d5f1279 | ||
|
11dcac6b04 | ||
|
5b9e5be295 | ||
|
0f6b403b43 | ||
|
03e510fa92 | ||
|
1dbaa61313 | ||
|
bfe2f03fbb | ||
|
702a52a93e | ||
|
eae6c96b5e | ||
|
fc2d96831d | ||
|
587ccd6a0d | ||
|
a4ff143600 | ||
|
a49456f44b | ||
|
f642a066f8 | ||
|
fa1b632a59 | ||
|
d3ca6c390c | ||
|
99578a333e | ||
|
c3070e5f39 | ||
|
dd7340d6d5 | ||
|
e7d6ab88ce | ||
|
16072b3af3 | ||
|
440ba24c57 | ||
|
37fc054739 | ||
|
8ad84c3aed | ||
|
2d7a7480d9 | ||
|
09aa0d96e1 | ||
|
3d24168ba7 | ||
|
323a554832 | ||
|
ffbc571660 | ||
|
efd1349a59 | ||
|
728ae813c5 | ||
|
0be8dbe7c5 | ||
|
270cf79bb7 | ||
|
0efc3d4ad7 | ||
|
0fb28001f9 | ||
|
207fee1a50 | ||
|
a366cbadee | ||
|
3c91aab9d8 | ||
|
3689fb51f8 | ||
|
e1e5c22b8a | ||
|
cb0ae6db6c | ||
|
39459b89cb | ||
|
5aaea62b54 | ||
|
baf9797907 | ||
|
dd630ba394 | ||
|
a640627fe1 | ||
|
5550af7e64 | ||
|
e0a1cf49e7 | ||
|
391b9cf6dc | ||
|
689010b714 | ||
|
c88c9e9aa0 | ||
|
90eb2ded1d | ||
|
7640c869de | ||
|
1ef7853488 | ||
|
e7692dc40b | ||
|
b9768e8100 | ||
|
d2adf39e04 | ||
|
dfa5f41984 | ||
|
49bfd5053f | ||
|
9c5a7d0e3e | ||
|
07680673f8 | ||
|
37de4dc447 | ||
|
55e28973d1 | ||
|
e7b0b8bbb1 | ||
|
3347cbf1da | ||
|
48f339f25b | ||
|
420d706979 | ||
|
8eaafe8f9b | ||
|
7b98a64035 | ||
|
403f02f961 | ||
|
abbb2fece7 | ||
|
e66b01b6db | ||
|
a21242db55 | ||
|
7e2a286b83 | ||
|
ed281eb743 | ||
|
d5720e3944 | ||
|
66338e2c83 | ||
|
4617654b57 | ||
|
d4785e0c74 | ||
|
e1fb7b9bd7 | ||
|
9822dcc4c0 | ||
|
447a60afd9 | ||
|
4f942ac890 | ||
|
02c5ccaae1 | ||
|
e8caede273 | ||
|
1c09694f1b | ||
|
6023acc10e | ||
|
466d07dccb | ||
|
547f4ea012 | ||
|
d7d6014789 | ||
|
ecd46cfa3c | ||
|
cd55a0b08c | ||
|
56ea4a5428 | ||
|
c850d7752f | ||
|
1d1babd2bb | ||
|
08f2dabc81 | ||
|
445b1bb295 | ||
|
4bed0af868 | ||
|
e3ae28369f | ||
|
48056a9a2e | ||
|
5011f59b34 | ||
|
052a77d2cc | ||
|
bcd5ab0dd2 | ||
|
5882affd3f | ||
|
034863ee38 | ||
|
b5e096180a | ||
|
54f46d6380 | ||
|
ab512e5086 | ||
|
e4bc5853ad | ||
|
cddf9fb9ba | ||
|
ee5231f567 | ||
58a3c3aeeb | |||
|
29f20c60c1 | ||
|
fba0775ebd | ||
|
3a98f3f5a1 | ||
|
c9181f571f | ||
|
e54d05a907 | ||
|
894aada3d2 | ||
|
929ab11738 | ||
|
7ad895b95c | ||
|
9a7ecd98ea | ||
|
dffd09e6f7 | ||
|
d03afd6bf8 | ||
|
58453a6b7d | ||
|
be87e5433c | ||
|
e630b34981 | ||
|
8db5386053 | ||
|
cbeba3fa0e | ||
|
a8fea3f657 | ||
|
5ac279e289 | ||
|
4480eeefbe | ||
|
4d1d799f98 | ||
|
ad8c59be2a | ||
|
545385da59 | ||
|
59ab4ccdb5 | ||
|
4d3274362c | ||
|
233dc424cb | ||
|
3a64cdd5cc | ||
|
0bae420e6b | ||
|
a4327579fa | ||
|
ff1afdb63c | ||
|
e0c1e338e4 | ||
|
b60a5462b7 | ||
|
3ff9d079d7 | ||
|
9113051840 | ||
|
a2dacc067b | ||
|
03c498c0fc | ||
|
59ac5769ad | ||
|
9752cb725e | ||
|
f36ec30cb3 | ||
|
77b3d74948 | ||
|
7e06aef932 | ||
|
e6bafa0b38 | ||
|
72350a8b4c | ||
|
5f87475f2d | ||
|
92b02c20db | ||
|
ddbc38c7fe | ||
|
eec8bf9f1c | ||
|
699bdbada3 | ||
|
d4ce000dc3 | ||
|
de710ef402 | ||
|
df17e1c788 | ||
|
5c3b876c04 | ||
|
f9aa56b18a | ||
|
9ced34a87c | ||
|
8f7b26cb5d | ||
|
bb307a64b6 | ||
|
59d54f3190 | ||
|
0b8a850086 | ||
|
7e03f423c3 | ||
|
d9a09bff40 | ||
|
afa992189d | ||
|
5314eda5ab | ||
|
8ae271c511 | ||
|
5280cd3aa3 | ||
|
7f4459c41c | ||
|
0f003fcefd | ||
|
72ed975d56 | ||
|
76d612faf4 | ||
|
dc536eef3d | ||
|
47258b02b1 | ||
|
7d46ac42b6 | ||
|
498d85a0f4 | ||
|
187ee602d6 | ||
|
de9b09251d | ||
|
4df50c7fc3 | ||
|
96304dbb17 | ||
|
b60646a1fc | ||
|
ed6f0d8f52 | ||
|
b742fb6659 | ||
|
d417d245f4 | ||
|
dd58a540a8 | ||
|
10272ca7a4 | ||
|
7de0792b97 | ||
|
eb0c1a8463 | ||
|
38832d9ce9 | ||
|
b0bef40386 | ||
|
8b418004eb | ||
|
60429490b3 | ||
|
11413ef3db | ||
|
85db95befd | ||
|
04d394c65c | ||
|
58be4a40fc | ||
|
f4bbeaec77 | ||
|
27a1153b91 | ||
|
32ec11a42e | ||
|
30bff0b52d | ||
|
f17fc3708d | ||
|
a9002a616e | ||
|
fc5c3c5441 | ||
|
3adc30477d | ||
|
c8c2bb909a | ||
|
0fa2d0110c | ||
|
13fb6bb141 | ||
|
8d231d34d4 | ||
|
4a5d21836a | ||
|
67f05bad27 | ||
|
fc1a226791 | ||
|
32940d3277 | ||
|
749e92694e | ||
|
a81ed64915 | ||
70db08cf07 | |||
2619eff6ad | |||
847f96c13e | |||
af8cfc1c02 | |||
|
7763269af8 | ||
|
7504300d9e | ||
|
0ac17fe543 | ||
|
26f903ab77 | ||
|
8244eb0bb8 | ||
|
18824e0e18 | ||
|
9f03b72b69 | ||
|
8d8b508d09 | ||
|
f8a8a57cf0 | ||
|
e595dbd525 | ||
|
f5aa7401b6 | ||
|
3c351f6968 | ||
|
8bc9738ccb | ||
|
000a2e0cb6 | ||
|
abd557943c | ||
|
39d3f7edfe | ||
|
57719a038c | ||
|
0e1bf0abf2 | ||
f2b9d9d84e | |||
bfd363df5d | |||
|
30789c608e | ||
a7290aedee | |||
|
349ab57afe | ||
|
85d46bc1b3 | ||
|
f682378cf7 | ||
|
291b1e1253 | ||
|
02f49093a3 | ||
564a8739d0 | |||
|
dad9275a30 | ||
|
6222e25adc | ||
|
bea7d8e651 | ||
|
39ab0eaf45 | ||
|
b29d19966f | ||
|
63ca187aa7 | ||
|
da053d7282 | ||
|
1c3717eb84 | ||
|
a03e519c61 | ||
|
2754a9fca3 | ||
|
b4e3c05c87 | ||
|
c54062f4bf | ||
|
016bc9a45b | ||
|
df74f5ebe7 | ||
|
333392f284 | ||
|
c48d55ba9a | ||
|
6ee0d8a267 | ||
|
d834819f79 | ||
|
60964a00b2 | ||
|
d03fb11f4c | ||
|
0e2bfeb671 | ||
|
ab285417ae | ||
|
02e1276df8 | ||
|
a8b13748d6 | ||
|
fadebd39ea | ||
6439c82eb6 | |||
|
472272129b | ||
|
d4b434a68f | ||
|
f4926cf569 | ||
|
d3256f3c54 | ||
|
0dc31808e7 | ||
|
45ec7b6263 | ||
|
22cd16bb46 | ||
|
42b2142ab7 | ||
|
2c698304ca | ||
|
af82399f37 | ||
|
1145bca042 | ||
|
e6c3fcaa0d | ||
|
3c86758cb5 | ||
|
b3e0da6fe9 | ||
|
7f1c337ee8 | ||
|
b72d5a4c26 | ||
|
a32a8718d7 | ||
|
9839f51ee1 | ||
|
74bb2527a9 | ||
|
cfcb7c041d | ||
|
a847cbde3b | ||
321e3382f4 | |||
|
b025a21e13 | ||
|
34513f3b38 | ||
|
cb007d62a1 | ||
|
a53a8ecb82 | ||
|
70b01a7cf1 | ||
|
99f15679a6 | ||
dd20073a9b | |||
|
189999b7c2 | ||
|
904ce1c4cd | ||
|
c69433bf0b | ||
|
23fa3c5e8e | ||
|
731e52c440 | ||
|
919497148a | ||
|
0832a8ea34 | ||
|
830d3bf3be | ||
|
ff56b757e6 | ||
|
608483ae60 | ||
|
ee5b6be4bb | ||
|
ad7574be59 | ||
|
14a1157508 | ||
|
7e2dda750e | ||
|
170310d7e5 | ||
|
82179a5f10 | ||
|
02ec735687 | ||
06739eb001 | |||
bd8cfd300a | |||
|
3ef6a4204f | ||
|
f5bde75eda | ||
|
6f126f4857 | ||
|
22085d94f1 | ||
|
de7afad3d4 | ||
|
bf6a28a95e | ||
|
e491de0a93 | ||
|
f5412fbfec | ||
|
44fb1efc73 | ||
|
2d7c3b51c7 | ||
|
8fc981cb92 | ||
|
de43192453 | ||
|
0515a63645 | ||
|
3b1eefaabb | ||
|
f1dd55b29e | ||
|
9411e7bd45 | ||
|
dc02404184 | ||
|
bb11567948 | ||
|
cacdc13d15 | ||
|
824648321c | ||
|
ee79935a8a | ||
|
b3591867e7 | ||
|
bf03119373 | ||
|
fc296bda77 | ||
|
cc45750c0c | ||
|
05960b1da1 | ||
|
f516658a5e | ||
|
db181bde4d | ||
|
8068a506b9 | ||
|
5c4cfb4053 | ||
|
f04761ff42 | ||
|
adbf8aa7d1 | ||
|
1e03df2aab | ||
|
0e4ff8a55c | ||
|
bc309fe725 | ||
|
378f710cee | ||
|
aa3673ed33 | ||
|
c489d7ca91 | ||
|
99fad892f0 | ||
|
8d10aa7e80 | ||
|
f2dee9e0a7 | ||
|
a1a084aabb | ||
|
cca447ca5e | ||
|
4bc4b38489 | ||
|
ed8c8acc89 | ||
|
b3be8f9d11 | ||
|
3852fab64b | ||
|
65fd9c71c6 | ||
|
43c709cd9f | ||
|
a6b6c8120b | ||
|
d79275b285 | ||
|
27924e246d | ||
|
94de049184 | ||
|
43180640f2 | ||
|
67b6a3606f | ||
|
d563617998 | ||
|
6592144584 | ||
|
26ac2b09fb | ||
|
156e25ed6a | ||
|
f9490c1329 | ||
|
93736565cb | ||
|
431849414f | ||
|
ec91f61403 | ||
|
32a595222d | ||
|
4540b88094 | ||
|
0cc084b58b | ||
|
858f446597 | ||
|
3a9db65b53 | ||
|
2e45deb8d3 | ||
|
6d772562ca | ||
|
ba33218ddf | ||
|
b6621da410 | ||
280b58dfd8 | |||
|
dccb7f8b47 | ||
|
2c2a058126 | ||
|
27b2e8584d | ||
|
b2c354a32c | ||
|
6867ef1e9a | ||
|
71d8cb6246 | ||
5e6c0a4c52 | |||
e46dc12efc | |||
|
45345b810c | ||
9c371b8181 | |||
|
43467b6978 | ||
|
05eb1c38a0 | ||
|
90b064fd3a | ||
|
5cd39b919b | ||
|
4db0fc1f5f | ||
|
73ad7901b0 | ||
|
d3e069ddc0 | ||
|
4a0d4bb093 | ||
|
60523b985a | ||
|
82a1a400d9 | ||
d78b31d34e | |||
0679b146a3 | |||
|
2821cb36b5 | ||
|
eadac4c033 |
2223 changed files with 138323 additions and 176545 deletions
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -3,7 +3,7 @@ name: Feedback
|
|||
about: Report bugs or suggest new features
|
||||
title: ''
|
||||
labels:
|
||||
assignees: kode54, nevack
|
||||
assignees: kode54
|
||||
|
||||
---
|
||||
|
||||
|
|
10
.github/workflows/debug.yml
vendored
10
.github/workflows/debug.yml
vendored
|
@ -11,12 +11,16 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
name: Build Universal Cog.app
|
||||
runs-on: macos-12
|
||||
runs-on: macos-15
|
||||
env:
|
||||
XCODE_DERIVEDDATA_PATH: build
|
||||
steps:
|
||||
- name: Switch to Xcode 16
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 16
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Unpack libraries
|
||||
|
@ -46,7 +50,7 @@ jobs:
|
|||
$XCODE_DERIVEDDATA_PATH/Build/Products/Debug/Cog.app
|
||||
$XCODE_DERIVEDDATA_PATH/Cog.zip
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Cog
|
||||
path: ${{ env.XCODE_DERIVEDDATA_PATH }}/Cog.zip
|
||||
|
|
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -1,12 +1,13 @@
|
|||
.DS_Store
|
||||
xcuserdata
|
||||
./build
|
||||
/build
|
||||
|
||||
# Special cog exceptions
|
||||
!Frameworks/OpenMPT/OpenMPT/build
|
||||
|
||||
# User-specific xcconfig files
|
||||
Xcode-config/DEVELOPMENT_TEAM.xcconfig
|
||||
Xcode-config/SENTRY_SETTINGS.xcconfig
|
||||
|
||||
# Plist derived from template at build time
|
||||
/Info.plist
|
||||
|
@ -19,6 +20,7 @@ Xcode-config/DEVELOPMENT_TEAM.xcconfig
|
|||
|
||||
# The project will unpack these before building, if necessary
|
||||
/ThirdParty/BASS/libbass.dylib
|
||||
/ThirdParty/BASS/libbass_mpc.dylib
|
||||
/ThirdParty/BASS/libbassflac.dylib
|
||||
/ThirdParty/BASS/libbassmidi.dylib
|
||||
/ThirdParty/BASS/libbassopus.dylib
|
||||
|
@ -30,10 +32,10 @@ Xcode-config/DEVELOPMENT_TEAM.xcconfig
|
|||
/ThirdParty/fdk-aac/lib/libfdk-aac.dylib
|
||||
/ThirdParty/fdk-aac/lib/libfdk-aac.la
|
||||
/ThirdParty/fdk-aac/lib/pkgconfig/fdk-aac.pc
|
||||
/ThirdParty/ffmpeg/lib/libavcodec.59.dylib
|
||||
/ThirdParty/ffmpeg/lib/libavformat.59.dylib
|
||||
/ThirdParty/ffmpeg/lib/libavutil.57.dylib
|
||||
/ThirdParty/ffmpeg/lib/libswresample.4.dylib
|
||||
/ThirdParty/ffmpeg/lib/libavcodec.61.dylib
|
||||
/ThirdParty/ffmpeg/lib/libavformat.61.dylib
|
||||
/ThirdParty/ffmpeg/lib/libavutil.59.dylib
|
||||
/ThirdParty/ffmpeg/lib/libswresample.5.dylib
|
||||
/ThirdParty/flac/lib/libFLAC.12.dylib
|
||||
/ThirdParty/libid3tag/lib/libid3tag.a
|
||||
/ThirdParty/libmad/lib/libmad.a
|
||||
|
@ -46,7 +48,9 @@ Xcode-config/DEVELOPMENT_TEAM.xcconfig
|
|||
/ThirdParty/ogg/lib/libogg.0.dylib
|
||||
/ThirdParty/opus/lib/libopus.0.dylib
|
||||
/ThirdParty/opusfile/lib/libopusfile.0.dylib
|
||||
/ThirdParty/rubberband/lib/librubberband.3.dylib
|
||||
/ThirdParty/speex/libspeex.a
|
||||
/ThirdParty/vorbis/lib/libvorbisfile.3.dylib
|
||||
/ThirdParty/vorbis/lib/libvorbis.0.dylib
|
||||
/ThirdParty/soxr/lib/libsoxr.0.dylib
|
||||
/ThirdParty/WavPack/lib/libwavpack.a
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
IBOutlet NSMenuItem *showArtistColumn;
|
||||
IBOutlet NSMenuItem *showAlbumColumn;
|
||||
IBOutlet NSMenuItem *showGenreColumn;
|
||||
IBOutlet NSMenuItem *showPlayCountColumn;
|
||||
IBOutlet NSMenuItem *showLengthColumn;
|
||||
IBOutlet NSMenuItem *showTrackColumn;
|
||||
IBOutlet NSMenuItem *showYearColumn;
|
||||
|
@ -66,11 +67,6 @@
|
|||
- (IBAction)delEntries:(id)sender;
|
||||
- (IBAction)savePlaylist:(id)sender;
|
||||
|
||||
- (IBAction)openLiberapayPage:(id)sender;
|
||||
- (IBAction)openPaypalPage:(id)sender;
|
||||
- (IBAction)openKofiPage:(id)sender;
|
||||
- (IBAction)openPatreonPage:(id)sender;
|
||||
|
||||
- (IBAction)privacyPolicy:(id)sender;
|
||||
|
||||
- (IBAction)feedback:(id)sender;
|
||||
|
@ -107,7 +103,10 @@
|
|||
- (void)showPathSuggester;
|
||||
+ (void)globalShowPathSuggester;
|
||||
|
||||
- (IBAction)checkForUpdates:(id)sender;
|
||||
- (void)selectTrack:(id)sender;
|
||||
|
||||
- (IBAction)showRubberbandSettings:(id)sender;
|
||||
+ (void)globalShowRubberbandSettings;
|
||||
|
||||
@property NSWindow *mainWindow;
|
||||
@property NSWindow *miniWindow;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#import "PlaylistEntry.h"
|
||||
#import "PlaylistLoader.h"
|
||||
#import "PlaylistView.h"
|
||||
#import "RubberbandEngineTransformer.h"
|
||||
#import "SQLiteStore.h"
|
||||
#import "SandboxBroker.h"
|
||||
#import "SpotlightWindowController.h"
|
||||
|
@ -27,12 +28,13 @@
|
|||
|
||||
#import "Shortcuts.h"
|
||||
#import <MASShortcut/Shortcut.h>
|
||||
|
||||
#import <Sparkle/Sparkle.h>
|
||||
#import <MASShortcut/MASDictionaryTransformer.h>
|
||||
|
||||
#import "PreferencesController.h"
|
||||
|
||||
@import Firebase;
|
||||
#import "FeedbackController.h"
|
||||
|
||||
@import Sentry;
|
||||
|
||||
void *kAppControllerContext = &kAppControllerContext;
|
||||
|
||||
|
@ -40,22 +42,6 @@ BOOL kAppControllerShuttingDown = NO;
|
|||
|
||||
static AppController *kAppController = nil;
|
||||
|
||||
@interface SparkleBridge : NSObject
|
||||
+ (SPUStandardUpdaterController *)sharedStandardUpdaterController;
|
||||
@end
|
||||
|
||||
@implementation SparkleBridge
|
||||
|
||||
+ (SPUStandardUpdaterController *)sharedStandardUpdaterController {
|
||||
static SPUStandardUpdaterController *sharedStandardUpdaterController_ = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedStandardUpdaterController_ = [[SPUStandardUpdaterController alloc] initWithUpdaterDelegate: nil userDriverDelegate: nil];
|
||||
});
|
||||
return sharedStandardUpdaterController_;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation AppController {
|
||||
BOOL _isFullToolbarStyle;
|
||||
}
|
||||
|
@ -89,6 +75,14 @@ static AppController *kAppController = nil;
|
|||
NSValueTransformer *numberHertzToStringTransformer = [[NumberHertzToStringTransformer alloc] init];
|
||||
[NSValueTransformer setValueTransformer:numberHertzToStringTransformer
|
||||
forName:@"NumberHertzToStringTransformer"];
|
||||
|
||||
NSValueTransformer *rubberbandEngineEnabledTransformer = [[RubberbandEngineEnabledTransformer alloc] init];
|
||||
[NSValueTransformer setValueTransformer:rubberbandEngineEnabledTransformer
|
||||
forName:@"RubberbandEngineEnabledTransformer"];
|
||||
|
||||
NSValueTransformer *rubberbandEngineHiddenTransformer = [[RubberbandEngineHiddenTransformer alloc] init];
|
||||
[NSValueTransformer setValueTransformer:rubberbandEngineHiddenTransformer
|
||||
forName:@"RubberbandEngineHiddenTransformer"];
|
||||
}
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
@ -175,22 +169,13 @@ static AppController *kAppController = nil;
|
|||
return [key isEqualToString:@"currentEntry"];
|
||||
}
|
||||
|
||||
static BOOL consentLastEnabled = NO;
|
||||
|
||||
- (void)awakeFromNib {
|
||||
#if DEBUG
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions": @(NO) }];
|
||||
#else
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions": @(YES) }];
|
||||
#endif
|
||||
|
||||
[FIRApp configure];
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.crashlyticsConsented" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kAppControllerContext];
|
||||
|
||||
#ifdef DEBUG
|
||||
// Prevent updates automatically in debug builds
|
||||
[[[SparkleBridge sharedStandardUpdaterController] updater] setAutomaticallyChecksForUpdates:NO];
|
||||
#endif
|
||||
[[[SparkleBridge sharedStandardUpdaterController] updater] setUpdateCheckInterval:3600];
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"sentryConsented": @(NO),
|
||||
@"sentryAskedConsent": @(NO) }];
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.sentryConsented" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kAppControllerContext];
|
||||
|
||||
[[totalTimeField cell] setBackgroundStyle:NSBackgroundStyleRaised];
|
||||
|
||||
|
@ -201,6 +186,10 @@ static AppController *kAppController = nil;
|
|||
[randomizeButton setToolTip:NSLocalizedString(@"RandomizeButtonTooltip", @"")];
|
||||
[fileButton setToolTip:NSLocalizedString(@"FileButtonTooltip", @"")];
|
||||
|
||||
[self registerDefaultHotKeys];
|
||||
|
||||
[self migrateHotKeys];
|
||||
|
||||
[self registerHotKeys];
|
||||
|
||||
(void)[spotlightWindowController init];
|
||||
|
@ -221,8 +210,15 @@ static AppController *kAppController = nil;
|
|||
[playlistLoader addDatabase];
|
||||
} else if([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]]) {
|
||||
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:newFilename]]];
|
||||
} else {
|
||||
} else if([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:oldFilename]]){
|
||||
/* Without the above check, it appears the code was retrieving a nil NSURL from the nonexistent path
|
||||
* Then adding it to the playlist and crashing further down the line
|
||||
* Nobody on a new setup should be seeing this open anything, so it should fall through to the
|
||||
* notice below.
|
||||
*/
|
||||
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:oldFilename]]];
|
||||
} else {
|
||||
ALog(@"No playlist found, leaving it empty.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +245,7 @@ static AppController *kAppController = nil;
|
|||
NSError *error = nil;
|
||||
NSArray *results = [playlistController.persistentContainer.viewContext executeFetchRequest:request error:&error];
|
||||
|
||||
if(results && [results count] == 1) {
|
||||
if(results && [results count] > 0) {
|
||||
PlaylistEntry *pe = results[0];
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"resumePlaybackOnStartup"]) {
|
||||
[playbackController playEntryAtIndex:pe.index startPaused:(lastStatus == CogStatusPaused) andSeekTo:@(pe.currentPosition)];
|
||||
|
@ -260,6 +256,13 @@ static AppController *kAppController = nil;
|
|||
pe.countAdded = NO;
|
||||
[playlistController commitPersistentStore];
|
||||
}
|
||||
// Bug fix
|
||||
if([results count] > 1) {
|
||||
for(size_t i = 1; i < [results count]; ++i) {
|
||||
PlaylistEntry *pe = results[i];
|
||||
[pe setCurrent:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,8 +286,8 @@ static AppController *kAppController = nil;
|
|||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeExpanded:) name:NSOutlineViewItemDidExpandNotification object:outlineView];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeCollapsed:) name:NSOutlineViewItemDidCollapseNotification object:outlineView];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDockMenu:) name:CogPlaybackDidBeginNotficiation object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDockMenu:) name:CogPlaybackDidStopNotficiation object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDockMenu:) name:CogPlaybackDidBeginNotificiation object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateDockMenu:) name:CogPlaybackDidStopNotificiation object:nil];
|
||||
|
||||
[self updateDockMenu:nil];
|
||||
|
||||
|
@ -329,10 +332,45 @@ static AppController *kAppController = nil;
|
|||
return;
|
||||
}
|
||||
|
||||
if([keyPath isEqualToString:@"values.crashlyticsConsented"]) {
|
||||
BOOL enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"crashlyticsConsented"];
|
||||
[[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:enabled];
|
||||
[FIRAnalytics setAnalyticsCollectionEnabled:enabled];
|
||||
if([keyPath isEqualToString:@"values.sentryConsented"]) {
|
||||
BOOL enabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"sentryConsented"];
|
||||
if(enabled != consentLastEnabled) {
|
||||
if(enabled) {
|
||||
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
|
||||
options.dsn = @"https://b5eda1c2390eb965a74dd735413b6392@cog-analytics.losno.co/3";
|
||||
options.debug = YES; // Enabled debug when first installing is always helpful
|
||||
|
||||
// Temporary until there's a better solution
|
||||
options.enableAppHangTracking = NO;
|
||||
|
||||
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
|
||||
// We recommend adjusting this value in production.
|
||||
options.tracesSampleRate = @1.0;
|
||||
options.profilesSampleRate = @1.0;
|
||||
|
||||
// Adds IP for users.
|
||||
// For more information, visit: https://docs.sentry.io/platforms/apple/data-management/data-collected/
|
||||
options.sendDefaultPii = YES;
|
||||
|
||||
// And now to set up user feedback prompting
|
||||
options.onCrashedLastRun = ^void(SentryEvent * _Nonnull event) {
|
||||
// capture user feedback
|
||||
FeedbackController *fbcon = [[FeedbackController alloc] init];
|
||||
[fbcon performSelectorOnMainThread:@selector(showWindow:) withObject:nil waitUntilDone:YES];
|
||||
if([fbcon waitForCompletion]) {
|
||||
SentryFeedback *feedback = [[SentryFeedback alloc] initWithMessage:[fbcon comments] name:[fbcon name] email:[fbcon email] source:SentryFeedbackSourceCustom associatedEventId:event.eventId attachments:nil];
|
||||
|
||||
[SentrySDK captureFeedback:feedback];
|
||||
}
|
||||
};
|
||||
}];
|
||||
} else {
|
||||
if([SentrySDK isEnabled]) {
|
||||
[SentrySDK close];
|
||||
}
|
||||
}
|
||||
consentLastEnabled = enabled;
|
||||
}
|
||||
} else if([keyPath isEqualToString:@"playlistController.currentEntry"]) {
|
||||
PlaylistEntry *entry = playlistController.currentEntry;
|
||||
NSString *appTitle = NSLocalizedString(@"CogTitle", @"");
|
||||
|
@ -562,22 +600,6 @@ static AppController *kAppController = nil;
|
|||
[theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
|
||||
}
|
||||
|
||||
- (IBAction)openLiberapayPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://liberapay.com/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)openPaypalPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/paypalme/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)openKofiPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://ko-fi.com/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)openPatreonPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.patreon.com/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)privacyPolicy:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:NSLocalizedString(@"PrivacyPolicyURL", @"Privacy policy URL from Iubenda.")]];
|
||||
}
|
||||
|
@ -612,7 +634,7 @@ static AppController *kAppController = nil;
|
|||
NSString *feedURLdefault = @"https://cogcdn.cog.losno.co/mercury.xml";
|
||||
[userDefaultsValuesDict setObject:feedURLdefault forKey:@"SUFeedURL"];
|
||||
|
||||
[userDefaultsValuesDict setObject:@"clearAndPlay" forKey:@"openingFilesBehavior"];
|
||||
[userDefaultsValuesDict setObject:@"enqueueAndPlay" forKey:@"openingFilesBehavior"];
|
||||
[userDefaultsValuesDict setObject:@"enqueue" forKey:@"openingFilesAlteredBehavior"];
|
||||
|
||||
[userDefaultsValuesDict setObject:@"albumGainWithPeak" forKey:@"volumeScaling"];
|
||||
|
@ -621,7 +643,7 @@ static AppController *kAppController = nil;
|
|||
|
||||
[userDefaultsValuesDict setObject:@(CogStatusStopped) forKey:@"lastPlaybackStatus"];
|
||||
|
||||
[userDefaultsValuesDict setObject:@"dls appl" forKey:@"midiPlugin"];
|
||||
[userDefaultsValuesDict setObject:@"BASSMIDI" forKey:@"midiPlugin"];
|
||||
|
||||
[userDefaultsValuesDict setObject:@"default" forKey:@"midi.flavor"];
|
||||
|
||||
|
@ -646,6 +668,9 @@ static AppController *kAppController = nil;
|
|||
[userDefaultsValuesDict setObject:@(2) forKey:@"synthDefaultLoopCount"];
|
||||
[userDefaultsValuesDict setObject:@(44100) forKey:@"synthSampleRate"];
|
||||
|
||||
[userDefaultsValuesDict setObject:@NO forKey:@"alwaysStopAfterCurrent"];
|
||||
[userDefaultsValuesDict setObject:@NO forKey:@"selectionFollowsPlayback"];
|
||||
|
||||
// Register and sync defaults
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
@ -674,9 +699,100 @@ static AppController *kAppController = nil;
|
|||
if([[[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"] isEqualToString:@"FluidSynth"]) {
|
||||
[[NSUserDefaults standardUserDefaults] setValue:@"BASSMIDI" forKey:@"midiPlugin"];
|
||||
}
|
||||
|
||||
NSString *midiPlugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"];
|
||||
if([midiPlugin length] == 8 && [[midiPlugin substringFromIndex:4] isEqualToString:@"appl"]) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"BASSMIDI" forKey:@"midiPlugin"];
|
||||
}
|
||||
}
|
||||
|
||||
MASShortcut *shortcutWithMigration(NSString *oldKeyCodePrefName,
|
||||
NSString *oldKeyModifierPrefName,
|
||||
NSString *newShortcutPrefName,
|
||||
NSInteger newDefaultKeyCode) {
|
||||
NSEventModifierFlags defaultModifiers = NSEventModifierFlagControl | NSEventModifierFlagCommand;
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
if([defaults objectForKey:oldKeyCodePrefName]) {
|
||||
NSInteger oldKeyCode = [defaults integerForKey:oldKeyCodePrefName];
|
||||
NSEventModifierFlags oldKeyModifiers = [defaults integerForKey:oldKeyModifierPrefName];
|
||||
// Should we consider temporarily save these values for further migration?
|
||||
[defaults removeObjectForKey:oldKeyCodePrefName];
|
||||
[defaults removeObjectForKey:oldKeyModifierPrefName];
|
||||
return [MASShortcut shortcutWithKeyCode:oldKeyCode modifierFlags:oldKeyModifiers];
|
||||
} else {
|
||||
return [MASShortcut shortcutWithKeyCode:newDefaultKeyCode modifierFlags:defaultModifiers];
|
||||
}
|
||||
}
|
||||
|
||||
static NSDictionary *shortcutDefaults = nil;
|
||||
|
||||
- (void)registerDefaultHotKeys {
|
||||
MASShortcut *playShortcut = shortcutWithMigration(@"hotKeyPlayKeyCode",
|
||||
@"hotKeyPlayModifiers",
|
||||
CogPlayShortcutKey,
|
||||
kVK_ANSI_P);
|
||||
MASShortcut *nextShortcut = shortcutWithMigration(@"hotKeyNextKeyCode",
|
||||
@"hotKeyNextModifiers",
|
||||
CogNextShortcutKey,
|
||||
kVK_ANSI_N);
|
||||
MASShortcut *prevShortcut = shortcutWithMigration(@"hotKeyPreviousKeyCode",
|
||||
@"hotKeyPreviousModifiers",
|
||||
CogPrevShortcutKey,
|
||||
kVK_ANSI_R);
|
||||
MASShortcut *spamShortcut = [MASShortcut shortcutWithKeyCode:kVK_ANSI_C
|
||||
modifierFlags:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
MASShortcut *fadeShortcut = [MASShortcut shortcutWithKeyCode:kVK_ANSI_O
|
||||
modifierFlags:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
MASShortcut *seekBkwdShortcut = [MASShortcut shortcutWithKeyCode:kVK_LeftArrow
|
||||
modifierFlags:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
MASShortcut *seekFwdShortcut = [MASShortcut shortcutWithKeyCode:kVK_RightArrow
|
||||
modifierFlags:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
|
||||
MASDictionaryTransformer *transformer = [MASDictionaryTransformer new];
|
||||
NSDictionary *playShortcutDict = [transformer reverseTransformedValue:playShortcut];
|
||||
NSDictionary *nextShortcutDict = [transformer reverseTransformedValue:nextShortcut];
|
||||
NSDictionary *prevShortcutDict = [transformer reverseTransformedValue:prevShortcut];
|
||||
NSDictionary *spamShortcutDict = [transformer reverseTransformedValue:spamShortcut];
|
||||
NSDictionary *fadeShortcutDict = [transformer reverseTransformedValue:fadeShortcut];
|
||||
NSDictionary *seekBkwdShortcutDict = [transformer reverseTransformedValue:seekBkwdShortcut];
|
||||
NSDictionary *seekFwdShortcutDict = [transformer reverseTransformedValue:seekFwdShortcut];
|
||||
|
||||
// Register default values to be used for the first app start
|
||||
NSDictionary<NSString *, NSDictionary *> *defaultShortcuts = @{
|
||||
CogPlayShortcutKey: playShortcutDict,
|
||||
CogNextShortcutKey: nextShortcutDict,
|
||||
CogPrevShortcutKey: prevShortcutDict,
|
||||
CogSpamShortcutKey: spamShortcutDict,
|
||||
CogFadeShortcutKey: fadeShortcutDict,
|
||||
CogSeekBackwardShortcutKey: seekBkwdShortcutDict,
|
||||
CogSeekForwardShortcutKey: seekFwdShortcutDict
|
||||
};
|
||||
|
||||
shortcutDefaults = defaultShortcuts;
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultShortcuts];
|
||||
}
|
||||
|
||||
- (IBAction)resetHotkeys:(id)sender {
|
||||
[shortcutDefaults enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:obj forKey:key];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)migrateHotKeys {
|
||||
NSArray *inKeys = @[CogPlayShortcutKeyV1, CogNextShortcutKeyV1, CogPrevShortcutKeyV1, CogSpamShortcutKeyV1, CogFadeShortcutKeyV1, CogSeekBackwardShortcutKeyV1, CogSeekForwardShortcutKeyV1];
|
||||
NSArray *outKeys = @[CogPlayShortcutKey, CogNextShortcutKey, CogPrevShortcutKey, CogSpamShortcutKey, CogFadeShortcutKey, CogSeekBackwardShortcutKey, CogSeekForwardShortcutKey];
|
||||
for(size_t i = 0, j = [inKeys count]; i < j; ++i) {
|
||||
NSString *inKey = inKeys[i];
|
||||
NSString *outKey = outKeys[i];
|
||||
id value = [[NSUserDefaults standardUserDefaults] objectForKey:inKey];
|
||||
if(value && value != [NSNull null]) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:value forKey:outKey];
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:inKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unassign previous handler first, so dealloc can unregister it from the global map before the new instances are assigned */
|
||||
- (void)registerHotKeys {
|
||||
MASShortcutBinder *binder = [MASShortcutBinder sharedBinder];
|
||||
[binder bindShortcutWithDefaultsKey:CogPlayShortcutKey
|
||||
|
@ -698,6 +814,21 @@ static AppController *kAppController = nil;
|
|||
toAction:^{
|
||||
[self clickSpam];
|
||||
}];
|
||||
|
||||
[binder bindShortcutWithDefaultsKey:CogFadeShortcutKey
|
||||
toAction:^{
|
||||
[self clickFade];
|
||||
}];
|
||||
|
||||
[binder bindShortcutWithDefaultsKey:CogSeekBackwardShortcutKey
|
||||
toAction:^{
|
||||
[self clickSeekBack];
|
||||
}];
|
||||
|
||||
[binder bindShortcutWithDefaultsKey:CogSeekForwardShortcutKey
|
||||
toAction:^{
|
||||
[self clickSeekForward];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)clickPlay {
|
||||
|
@ -724,10 +855,22 @@ static AppController *kAppController = nil;
|
|||
[playbackController spam:nil];
|
||||
}
|
||||
|
||||
- (void)clickFade {
|
||||
[playbackController fade:nil];
|
||||
}
|
||||
|
||||
- (void)clickSeek:(NSTimeInterval)position {
|
||||
[playbackController seek:self toTime:position];
|
||||
}
|
||||
|
||||
- (void)clickSeekBack {
|
||||
[playbackController seekBackward:10.0];
|
||||
}
|
||||
|
||||
- (void)clickSeekForward {
|
||||
[playbackController seekForward:10.0];
|
||||
}
|
||||
|
||||
- (void)changeFontSize:(float)size {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
float fCurrentSize = [defaults floatForKey:@"fontSize"];
|
||||
|
@ -797,7 +940,7 @@ static AppController *kAppController = nil;
|
|||
|
||||
BOOL hideItem = NO;
|
||||
|
||||
if([[notification name] isEqualToString:CogPlaybackDidStopNotficiation] || !pe || ![pe artist] || [[pe artist] isEqualToString:@""])
|
||||
if([[notification name] isEqualToString:CogPlaybackDidStopNotificiation] || !pe || ![pe artist] || [[pe artist] isEqualToString:@""])
|
||||
hideItem = YES;
|
||||
|
||||
if(hideItem && [dockMenu indexOfItem:currentArtistItem] == 0) {
|
||||
|
@ -823,8 +966,21 @@ static AppController *kAppController = nil;
|
|||
[kAppController showPathSuggester];
|
||||
}
|
||||
|
||||
- (IBAction)checkForUpdates:(id)sender {
|
||||
[[SparkleBridge sharedStandardUpdaterController] checkForUpdates:[[NSApplication sharedApplication] delegate]];
|
||||
- (void)showRubberbandSettings:(id)sender {
|
||||
[preferencesController showRubberbandSettings:sender];
|
||||
}
|
||||
|
||||
+ (void)globalShowRubberbandSettings {
|
||||
[kAppController showRubberbandSettings:kAppController];
|
||||
}
|
||||
|
||||
- (void)selectTrack:(id)sender {
|
||||
PlaylistEntry *pe = (PlaylistEntry *)sender;
|
||||
@try {
|
||||
[playlistView selectRowIndexes:[NSIndexSet indexSetWithIndex:pe.index] byExtendingSelection:NO];
|
||||
}
|
||||
@catch(NSException *e) {
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
@interface DockIconController : NSObject {
|
||||
NSImage *dockImage;
|
||||
|
||||
NSInteger lastDockCustom;
|
||||
NSInteger lastDockCustomPlaque;
|
||||
NSInteger dockCustomLoaded;
|
||||
NSImage *dockCustomStop;
|
||||
NSImage *dockCustomPlay;
|
||||
NSImage *dockCustomPause;
|
||||
|
||||
IBOutlet PlaybackController *playbackController;
|
||||
|
||||
NSInteger lastPlaybackStatus;
|
||||
|
|
|
@ -14,16 +14,24 @@
|
|||
|
||||
static NSString *DockIconPlaybackStatusObservationContext = @"DockIconPlaybackStatusObservationContext";
|
||||
|
||||
static NSString *CogCustomDockIconsReloadNotification = @"CogCustomDockIconsReloadNotification";
|
||||
|
||||
- (void)startObserving {
|
||||
[playbackController addObserver:self forKeyPath:@"playbackStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[playbackController addObserver:self forKeyPath:@"progressOverall" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionOld) context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.colorfulDockIcons" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.customDockIcons" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.customDockIconsPlaque" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshDockIcons:) name:CogCustomDockIconsReloadNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)stopObserving {
|
||||
[playbackController removeObserver:self forKeyPath:@"playbackStatus" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[playbackController removeObserver:self forKeyPath:@"progressOverall" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.colorfulDockIcons" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.customDockIcons" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.customDockIconsPlaque" context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:CogCustomDockIconsReloadNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)startObservingProgress:(NSProgress *)progress {
|
||||
|
@ -42,6 +50,34 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
|
|||
}
|
||||
}
|
||||
|
||||
static NSString *getCustomIconName(NSString *baseName) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *basePath = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
||||
basePath = [basePath stringByAppendingPathComponent:@"Icons"];
|
||||
basePath = [basePath stringByAppendingPathComponent:baseName];
|
||||
return [basePath stringByAppendingPathExtension:@"png"];
|
||||
}
|
||||
|
||||
- (BOOL)loadCustomDockIcons {
|
||||
NSError *error = nil;
|
||||
NSData *dataStopIcon = [NSData dataWithContentsOfFile:getCustomIconName(@"Stop") options:(NSDataReadingMappedIfSafe) error:&error];
|
||||
if(!dataStopIcon || error) {
|
||||
return NO;
|
||||
}
|
||||
NSData *dataPlayIcon = [NSData dataWithContentsOfFile:getCustomIconName(@"Play") options:(NSDataReadingMappedIfSafe) error:&error];
|
||||
if(!dataPlayIcon || error) {
|
||||
return NO;
|
||||
}
|
||||
NSData *dataPauseIcon = [NSData dataWithContentsOfFile:getCustomIconName(@"Pause") options:(NSDataReadingMappedIfSafe) error:&error];
|
||||
if(!dataPauseIcon || error) {
|
||||
return NO;
|
||||
}
|
||||
dockCustomStop = [[NSImage alloc] initWithData:dataStopIcon];
|
||||
dockCustomPlay = [[NSImage alloc] initWithData:dataPlayIcon];
|
||||
dockCustomPause = [[NSImage alloc] initWithData:dataPauseIcon];
|
||||
return (dockCustomStop && dockCustomPlay && dockCustomPause);
|
||||
}
|
||||
|
||||
- (void)refreshDockIcon:(NSInteger)playbackStatus withProgress:(double)progressStatus {
|
||||
// Really weird crash user experienced because the plaque image didn't load?
|
||||
if(!dockImage || dockImage.size.width == 0 || dockImage.size.height == 0) return;
|
||||
|
@ -50,6 +86,30 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
|
|||
BOOL drawIcon = NO;
|
||||
BOOL removeProgress = NO;
|
||||
|
||||
BOOL useCustomDockIcons = [[NSUserDefaults standardUserDefaults] boolForKey:@"customDockIcons"];
|
||||
BOOL useCustomDockIconsPlaque = [[NSUserDefaults standardUserDefaults] boolForKey:@"customDockIconsPlaque"];
|
||||
|
||||
if(useCustomDockIcons && !dockCustomLoaded) {
|
||||
dockCustomLoaded = [self loadCustomDockIcons];
|
||||
if(!dockCustomLoaded) {
|
||||
useCustomDockIcons = NO;
|
||||
}
|
||||
}
|
||||
|
||||
if(useCustomDockIcons != lastDockCustom ||
|
||||
useCustomDockIconsPlaque != lastDockCustomPlaque) {
|
||||
lastDockCustom = useCustomDockIcons;
|
||||
lastDockCustomPlaque = useCustomDockIconsPlaque;
|
||||
drawIcon = YES;
|
||||
|
||||
if(!useCustomDockIcons) {
|
||||
dockCustomLoaded = NO;
|
||||
dockCustomStop = nil;
|
||||
dockCustomPlay = nil;
|
||||
dockCustomPause = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(playbackStatus < 0)
|
||||
playbackStatus = lastPlaybackStatus;
|
||||
else {
|
||||
|
@ -82,20 +142,20 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
|
|||
if(drawIcon) {
|
||||
switch(playbackStatus) {
|
||||
case CogStatusPlaying:
|
||||
badgeImage = [NSImage imageNamed:getBadgeName(@"Play", colorfulIcons)];
|
||||
badgeImage = useCustomDockIcons ? dockCustomPlay : [NSImage imageNamed:getBadgeName(@"Play", colorfulIcons)];
|
||||
break;
|
||||
case CogStatusPaused:
|
||||
badgeImage = [NSImage imageNamed:getBadgeName(@"Pause", colorfulIcons)];
|
||||
badgeImage = useCustomDockIcons ? dockCustomPause : [NSImage imageNamed:getBadgeName(@"Pause", colorfulIcons)];
|
||||
break;
|
||||
|
||||
default:
|
||||
badgeImage = [NSImage imageNamed:getBadgeName(@"Stop", colorfulIcons)];
|
||||
badgeImage = useCustomDockIcons ? dockCustomStop : [NSImage imageNamed:getBadgeName(@"Stop", colorfulIcons)];
|
||||
break;
|
||||
}
|
||||
|
||||
NSSize badgeSize = [badgeImage size];
|
||||
|
||||
NSImage *newDockImage = [dockImage copy];
|
||||
NSImage *newDockImage = (useCustomDockIcons && !useCustomDockIconsPlaque) ? [[NSImage alloc] initWithSize:NSMakeSize(1024, 1024)] : [dockImage copy];
|
||||
[newDockImage lockFocus];
|
||||
|
||||
[badgeImage drawInRect:NSMakeRect(0, 0, 1024, 1024)
|
||||
|
@ -186,7 +246,9 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
|
|||
}
|
||||
|
||||
[self refreshDockIcon:-1 withProgress:progressStatus];
|
||||
} else if([keyPath isEqualToString:@"values.colorfulDockIcons"]) {
|
||||
} else if([keyPath isEqualToString:@"values.colorfulDockIcons"] ||
|
||||
[keyPath isEqualToString:@"values.customDockIcons"] ||
|
||||
[keyPath isEqualToString:@"values.customDockIconsPlaque"]) {
|
||||
[self refreshDockIcon:-1 withProgress:-10];
|
||||
} else if([keyPath isEqualToString:@"fractionCompleted"]) {
|
||||
double progressStatus = [(NSProgress *)object fractionCompleted];
|
||||
|
@ -197,6 +259,12 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
|
|||
}
|
||||
}
|
||||
|
||||
- (void)refreshDockIcons:(NSNotification *)notification {
|
||||
lastDockCustom = NO;
|
||||
dockCustomLoaded = NO;
|
||||
[self refreshDockIcon:-1 withProgress:-10];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
dockImage = [[NSImage imageNamed:@"Plaque"] copy];
|
||||
lastColorfulStatus = -1;
|
||||
|
|
|
@ -19,10 +19,16 @@
|
|||
#define DEFAULT_VOLUME_DOWN 5
|
||||
#define DEFAULT_VOLUME_UP DEFAULT_VOLUME_DOWN
|
||||
|
||||
extern NSString *CogPlaybackDidBeginNotficiation;
|
||||
extern NSString *CogPlaybackDidPauseNotficiation;
|
||||
extern NSString *CogPlaybackDidResumeNotficiation;
|
||||
extern NSString *CogPlaybackDidStopNotficiation;
|
||||
#define DEFAULT_PITCH_DOWN 0.2
|
||||
#define DEFAULT_PITCH_UP DEFAULT_PITCH_DOWN
|
||||
|
||||
#define DEFAULT_TEMPO_DOWN 0.2
|
||||
#define DEFAULT_TEMPO_UP DEFAULT_TEMPO_DOWN
|
||||
|
||||
extern NSString *CogPlaybackDidBeginNotificiation;
|
||||
extern NSString *CogPlaybackDidPauseNotificiation;
|
||||
extern NSString *CogPlaybackDidResumeNotificiation;
|
||||
extern NSString *CogPlaybackDidStopNotificiation;
|
||||
|
||||
extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
|
||||
|
||||
|
@ -40,6 +46,9 @@ extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
|
|||
IBOutlet EqualizerWindowController *equalizerWindowController;
|
||||
|
||||
IBOutlet NSSlider *volumeSlider;
|
||||
IBOutlet NSSlider *pitchSlider;
|
||||
IBOutlet NSSlider *tempoSlider;
|
||||
IBOutlet NSButton *lockButton;
|
||||
|
||||
IBOutlet NSArrayController *outputDevices;
|
||||
|
||||
|
@ -69,6 +78,14 @@ extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
|
|||
- (IBAction)volumeDown:(id)sender;
|
||||
- (IBAction)volumeUp:(id)sender;
|
||||
|
||||
- (IBAction)changePitch:(id)sender;
|
||||
- (IBAction)pitchDown:(id)sender;
|
||||
- (IBAction)pitchUp:(id)sender;
|
||||
|
||||
- (IBAction)changeTempo:(id)sender;
|
||||
- (IBAction)tempoDown:(id)sender;
|
||||
- (IBAction)tempoUp:(id)sender;
|
||||
|
||||
- (IBAction)playPauseResume:(id)sender;
|
||||
- (IBAction)pauseResume:(id)sender;
|
||||
- (IBAction)skipToNextAlbum:(id)sender;
|
||||
|
|
|
@ -19,18 +19,76 @@
|
|||
|
||||
#import "Logging.h"
|
||||
|
||||
@import Firebase;
|
||||
@import Sentry;
|
||||
|
||||
// Sentry captureMessage is too spammy to use for anything but actual errors
|
||||
|
||||
extern BOOL kAppControllerShuttingDown;
|
||||
|
||||
@implementation NSObject (NxAdditions)
|
||||
|
||||
#if 0
|
||||
-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ...
|
||||
{
|
||||
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
||||
|
||||
// Setup the invocation
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
invocation.target = self;
|
||||
invocation.selector = selector;
|
||||
|
||||
// Associate the arguments
|
||||
va_list objects;
|
||||
va_start(objects, object);
|
||||
unsigned int objectCounter = 2;
|
||||
for (id obj = object; obj != nil; obj = va_arg(objects, id))
|
||||
{
|
||||
[invocation setArgument:&obj atIndex:objectCounter++];
|
||||
}
|
||||
va_end(objects);
|
||||
|
||||
// Make sure to invoke on a background queue
|
||||
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
|
||||
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
|
||||
[backgroundQueue addOperation:operation];
|
||||
}
|
||||
#endif
|
||||
|
||||
-(void)performSelectorOnMainThread:(SEL)selector withObjects:(id)object, ...
|
||||
{
|
||||
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
||||
|
||||
// Setup the invocation
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||
invocation.target = self;
|
||||
invocation.selector = selector;
|
||||
|
||||
// Associate the arguments
|
||||
va_list objects;
|
||||
va_start(objects, object);
|
||||
unsigned int objectCounter = 2;
|
||||
for (id obj = object; obj != nil; obj = va_arg(objects, id))
|
||||
{
|
||||
[invocation setArgument:&obj atIndex:objectCounter++];
|
||||
}
|
||||
va_end(objects);
|
||||
|
||||
// Invoke on the main operation queue
|
||||
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:invocation];
|
||||
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
|
||||
[mainQueue addOperation:operation];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlaybackController
|
||||
|
||||
#define DEFAULT_SEEK 5
|
||||
|
||||
NSString *CogPlaybackDidBeginNotficiation = @"CogPlaybackDidBeginNotficiation";
|
||||
NSString *CogPlaybackDidPauseNotficiation = @"CogPlaybackDidPauseNotficiation";
|
||||
NSString *CogPlaybackDidResumeNotficiation = @"CogPlaybackDidResumeNotficiation";
|
||||
NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
||||
NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotificiation";
|
||||
NSString *CogPlaybackDidPauseNotificiation = @"CogPlaybackDidPauseNotificiation";
|
||||
NSString *CogPlaybackDidResumeNotificiation = @"CogPlaybackDidResumeNotificiation";
|
||||
NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
|
||||
|
||||
@synthesize playbackStatus;
|
||||
|
||||
|
@ -62,15 +120,51 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
|
||||
- (void)initDefaults {
|
||||
NSDictionary *defaultsDictionary = @{ @"volume": @(75.0),
|
||||
@"pitch": @(1.0),
|
||||
@"tempo": @(1.0),
|
||||
@"speedLock": @(YES),
|
||||
@"GraphicEQenable": @(NO),
|
||||
@"GraphicEQpreset": @(-1),
|
||||
@"GraphicEQtrackgenre": @(NO),
|
||||
@"volumeLimit": @(YES),
|
||||
@"headphoneVirtualization": @(NO) };
|
||||
@"enableHrtf": @(NO),
|
||||
@"enableHeadTracking": @(NO),
|
||||
@"enableHDCD": @(NO),
|
||||
@"rubberbandEngine": @"disabled",
|
||||
@"rubberbandTransients": @"crisp",
|
||||
@"rubberbandDetector": @"compound",
|
||||
@"rubberbandPhase": @"laminar",
|
||||
@"rubberbandWindow": @"standard",
|
||||
@"rubberbandSmoothing": @"off",
|
||||
@"rubberbandFormant": @"shifted",
|
||||
@"rubberbandPitch": @"highspeed",
|
||||
@"rubberbandChannels": @"apart"
|
||||
};
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
|
||||
}
|
||||
|
||||
static double speedScale(double input, double min, double max) {
|
||||
input = (input - min) * 100.0 / (max - min);
|
||||
return ((input * input) * (5.0 - 0.2) / 10000.0) + 0.2;
|
||||
}
|
||||
|
||||
static double reverseSpeedScale(double input, double min, double max) {
|
||||
input = sqrtf((input - 0.2) * 10000.0 / (5.0 - 0.2));
|
||||
return (input * (max - min) / 100.0) + min;
|
||||
}
|
||||
|
||||
- (void)snapSpeeds {
|
||||
double pitch = [[NSUserDefaults standardUserDefaults] doubleForKey:@"pitch"];
|
||||
double tempo = [[NSUserDefaults standardUserDefaults] doubleForKey:@"tempo"];
|
||||
if(fabs(pitch - 1.0) < 1e-6) {
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:1.0 forKey:@"pitch"];
|
||||
}
|
||||
if(fabs(tempo - 1.0) < 1e-6) {
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:1.0 forKey:@"tempo"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
@ -80,6 +174,16 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
[volumeSlider setDoubleValue:logarithmicToLinear(volume, MAX_VOLUME)];
|
||||
[audioPlayer setVolume:volume];
|
||||
|
||||
double pitch = [[NSUserDefaults standardUserDefaults] doubleForKey:@"pitch"];
|
||||
[pitchSlider setDoubleValue:reverseSpeedScale(pitch, [pitchSlider minValue], [pitchSlider maxValue])];
|
||||
double tempo = [[NSUserDefaults standardUserDefaults] doubleForKey:@"tempo"];
|
||||
[tempoSlider setDoubleValue:reverseSpeedScale(tempo, [tempoSlider minValue], [tempoSlider maxValue])];
|
||||
|
||||
[self snapSpeeds];
|
||||
|
||||
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
|
||||
[lockButton setTitle:speedLock ? @"🔒" : @"🔓"];
|
||||
|
||||
[self setSeekable:NO];
|
||||
}
|
||||
|
||||
|
@ -101,6 +205,11 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
}
|
||||
|
||||
- (IBAction)pause:(id)sender {
|
||||
if(![self seekable]) {
|
||||
[self stop:sender];
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:CogStatusPaused forKey:@"lastPlaybackStatus"];
|
||||
|
||||
[audioPlayer pause];
|
||||
|
@ -192,11 +301,11 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
if(!pe.url) {
|
||||
pe.error = YES;
|
||||
pe.errorMessage = NSLocalizedStringFromTableInBundle(@"ErrorMessageBadFile", nil, [NSBundle bundleForClass:[self class]], @"");
|
||||
[[FIRCrashlytics crashlytics] log:@"Attempting to play bad file."];
|
||||
[SentrySDK captureMessage:@"Attempted to play a bad file with no URL"];
|
||||
return;
|
||||
}
|
||||
|
||||
[[FIRCrashlytics crashlytics] logWithFormat:@"Playing track: %@", pe.url];
|
||||
//[SentrySDK captureMessage:[NSString stringWithFormat:@"Playing track: %@", pe.url]];
|
||||
|
||||
DLog(@"PLAYLIST CONTROLLER: %@", [playlistController class]);
|
||||
[playlistController setCurrentEntry:pe];
|
||||
|
@ -216,7 +325,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
|
||||
#if 0
|
||||
// Race here, but the worst that could happen is we re-read the data
|
||||
if ([pe metadataLoaded] != YES) {
|
||||
if([pe metadataLoaded] != YES) {
|
||||
[pe performSelectorOnMainThread:@selector(setMetadata:) withObject:[playlistLoader readEntryInfo:pe] waitUntilDone:YES];
|
||||
}
|
||||
#elif 0
|
||||
|
@ -237,7 +346,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
|
||||
double seekTime = pe.seekable ? [offset doubleValue] : 0.0;
|
||||
|
||||
[audioPlayer play:pe.url withUserInfo:pe withRGInfo:makeRGInfo(pe) startPaused:paused andSeekTo:seekTime];
|
||||
[audioPlayer performSelectorOnMainThread:@selector(playBG:withUserInfo:withRGInfo:startPaused:andSeekTo:) withObjects:pe.url, pe, makeRGInfo(pe), @(paused), @(seekTime), nil];
|
||||
}
|
||||
|
||||
- (IBAction)next:(id)sender {
|
||||
|
@ -248,8 +357,12 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
|
||||
- (IBAction)prev:(id)sender {
|
||||
if([playlistController prev] == NO)
|
||||
return;
|
||||
double pos = [audioPlayer amountPlayed];
|
||||
|
||||
if(pos < 5.0) {
|
||||
if([playlistController prev] == NO)
|
||||
return;
|
||||
}
|
||||
|
||||
[self playEntry:[playlistController currentEntry]];
|
||||
}
|
||||
|
@ -266,9 +379,14 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
|
||||
- (IBAction)seek:(id)sender {
|
||||
if(![sender respondsToSelector:@selector(doubleValue)]) {
|
||||
ALog(@"Someone sent [PlaybackController seek:] a non-seekbar object: %@", sender);
|
||||
return;
|
||||
}
|
||||
|
||||
double time = [sender doubleValue];
|
||||
|
||||
[audioPlayer seekToTime:time];
|
||||
[audioPlayer performSelectorOnMainThread:@selector(seekToTimeBG:) withObjects:@(time), nil];
|
||||
|
||||
lastPosition = -10;
|
||||
|
||||
|
@ -285,7 +403,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
|
||||
lastPosition = -10;
|
||||
|
||||
[audioPlayer seekToTime:time];
|
||||
[audioPlayer performSelectorOnMainThread:@selector(seekToTimeBG:) withObjects:@(time), nil];
|
||||
|
||||
[self setPosition:time];
|
||||
|
||||
|
@ -311,14 +429,16 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
|
||||
- (void)seekForward:(double)amount {
|
||||
double seekTo = [audioPlayer amountPlayed] + amount;
|
||||
|
||||
if(seekTo > [[[playlistController currentEntry] length] doubleValue]) {
|
||||
[self next:self];
|
||||
} else {
|
||||
lastPosition = -10;
|
||||
[audioPlayer seekToTime:seekTo];
|
||||
[self setPosition:seekTo];
|
||||
if([self seekable]) {
|
||||
double seekTo = [audioPlayer amountPlayed] + amount;
|
||||
|
||||
if(seekTo > [[[playlistController currentEntry] length] doubleValue]) {
|
||||
[self next:self];
|
||||
} else {
|
||||
lastPosition = -10;
|
||||
[audioPlayer performSelectorOnMainThread:@selector(seekToTimeBG:) withObjects:@(seekTo), nil];
|
||||
[self setPosition:seekTo];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,15 +447,17 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
|
||||
- (void)seekBackward:(double)amount {
|
||||
double seekTo = [audioPlayer amountPlayed] - amount;
|
||||
|
||||
if(seekTo < 0)
|
||||
seekTo = 0;
|
||||
|
||||
lastPosition = -10;
|
||||
|
||||
[audioPlayer seekToTime:seekTo];
|
||||
[self setPosition:seekTo];
|
||||
if([self seekable]) {
|
||||
double seekTo = [audioPlayer amountPlayed] - amount;
|
||||
|
||||
if(seekTo < 0)
|
||||
seekTo = 0;
|
||||
|
||||
lastPosition = -10;
|
||||
|
||||
[audioPlayer performSelectorOnMainThread:@selector(seekToTimeBG:) withObjects:@(seekTo), nil];
|
||||
[self setPosition:seekTo];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -344,7 +466,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
NSImage *img = [NSImage imageNamed:name];
|
||||
// [img retain];
|
||||
|
||||
if (img == nil)
|
||||
if(img == nil)
|
||||
{
|
||||
DLog(@"Error loading image!");
|
||||
}
|
||||
|
@ -445,6 +567,32 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
}
|
||||
|
||||
- (IBAction)changePitch:(id)sender {
|
||||
const double pitch = speedScale([sender doubleValue], [pitchSlider minValue], [pitchSlider maxValue]);
|
||||
DLog(@"PITCH: %lf", pitch);
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:pitch forKey:@"pitch"];
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:pitch forKey:@"tempo"];
|
||||
}
|
||||
|
||||
[self snapSpeeds];
|
||||
}
|
||||
|
||||
- (IBAction)changeTempo:(id)sender {
|
||||
const double tempo = speedScale([sender doubleValue], [tempoSlider minValue], [tempoSlider maxValue]);
|
||||
DLog(@"TEMPO: %lf", tempo);
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:tempo forKey:@"tempo"];
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:tempo forKey:@"pitch"];
|
||||
}
|
||||
|
||||
[self snapSpeeds];
|
||||
}
|
||||
|
||||
- (IBAction)skipToNextAlbum:(id)sender {
|
||||
BOOL found = NO;
|
||||
|
||||
|
@ -546,8 +694,75 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq {
|
||||
- (IBAction)pitchDown:(id)sender {
|
||||
double pitch = speedScale([pitchSlider doubleValue], [pitchSlider minValue], [pitchSlider maxValue]);
|
||||
double newPitch = pitch - DEFAULT_PITCH_DOWN;
|
||||
if(newPitch < 0.2) {
|
||||
newPitch = 0.2;
|
||||
}
|
||||
[pitchSlider setDoubleValue:reverseSpeedScale(newPitch, [pitchSlider minValue], [pitchSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newPitch forKey:@"pitch"];
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
|
||||
[tempoSlider setDoubleValue:reverseSpeedScale(newPitch, [tempoSlider minValue], [tempoSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newPitch forKey:@"tempo"];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)pitchUp:(id)sender {
|
||||
double pitch = speedScale([pitchSlider doubleValue], [pitchSlider minValue], [pitchSlider maxValue]);
|
||||
double newPitch = pitch + DEFAULT_PITCH_UP;
|
||||
if(newPitch > 5.0) {
|
||||
newPitch = 5.0;
|
||||
}
|
||||
[pitchSlider setDoubleValue:reverseSpeedScale(newPitch, [pitchSlider minValue], [pitchSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newPitch forKey:@"pitch"];
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
|
||||
[tempoSlider setDoubleValue:reverseSpeedScale(newPitch, [tempoSlider minValue], [tempoSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newPitch forKey:@"tempo"];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)tempoDown:(id)sender {
|
||||
double tempo = speedScale([tempoSlider doubleValue], [tempoSlider minValue], [tempoSlider maxValue]);
|
||||
double newTempo = tempo - DEFAULT_TEMPO_DOWN;
|
||||
if(newTempo < 0.2) {
|
||||
newTempo = 0.2;
|
||||
}
|
||||
[tempoSlider setDoubleValue:reverseSpeedScale(newTempo, [tempoSlider minValue], [tempoSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newTempo forKey:@"tempo"];
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
|
||||
[pitchSlider setDoubleValue:reverseSpeedScale(newTempo, [pitchSlider minValue], [pitchSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newTempo forKey:@"pitch"];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)tempoUp:(id)sender {
|
||||
double tempo = speedScale([tempoSlider doubleValue], [tempoSlider minValue], [tempoSlider maxValue]);
|
||||
double newTempo = tempo + DEFAULT_TEMPO_UP;
|
||||
if(newTempo > 5.0) {
|
||||
newTempo = 5.0;
|
||||
}
|
||||
[tempoSlider setDoubleValue:reverseSpeedScale(newTempo, [tempoSlider minValue], [tempoSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newTempo forKey:@"tempo"];
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
|
||||
[pitchSlider setDoubleValue:reverseSpeedScale(newTempo, [pitchSlider minValue], [pitchSlider maxValue])];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:newTempo forKey:@"pitch"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq {
|
||||
if(_eq && _eq != eq) {
|
||||
[equalizerWindowController setEQ:nil];
|
||||
}
|
||||
|
@ -586,15 +801,15 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
|
||||
if(pe && pe.url) {
|
||||
[[FIRCrashlytics crashlytics] logWithFormat:@"Beginning decoding track: %@", pe.url];
|
||||
//[SentrySDK captureMessage:[NSString stringWithFormat:@"Beginning decoding track: %@", pe.url]];
|
||||
[player setNextStream:pe.url withUserInfo:pe withRGInfo:makeRGInfo(pe)];
|
||||
} else if(pe) {
|
||||
[[FIRCrashlytics crashlytics] log:@"Invalid playlist entry reached."];
|
||||
[SentrySDK captureMessage:@"Invalid playlist entry reached"];
|
||||
[player setNextStream:nil];
|
||||
pe.error = YES;
|
||||
pe.errorMessage = NSLocalizedStringFromTableInBundle(@"ErrorMessageBadFile", nil, [NSBundle bundleForClass:[self class]], @"");
|
||||
} else {
|
||||
[[FIRCrashlytics crashlytics] log:@"End of playlist reached."];
|
||||
//[SentrySDK captureMessage:@"End of playlist reached"];
|
||||
[player setNextStream:nil];
|
||||
}
|
||||
}
|
||||
|
@ -605,7 +820,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
// Delay the action until this function has returned to the audio thread
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
|
||||
if(pe) {
|
||||
[[FIRCrashlytics crashlytics] logWithFormat:@"Updating UI with track: %@", pe.url];
|
||||
//[SentrySDK captureMessage:[NSString stringWithFormat:@"Updating UI with track: %@", pe.url]];
|
||||
}
|
||||
|
||||
[self->playlistController setCurrentEntry:pe];
|
||||
|
@ -622,7 +837,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
});
|
||||
|
||||
if(pe) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidBeginNotficiation object:pe];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidBeginNotificiation object:pe];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -636,26 +851,26 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
|
||||
if(status == CogStatusStopped) {
|
||||
[[FIRCrashlytics crashlytics] log:@"Stopped."];
|
||||
//[SentrySDK captureMessage:@"Playback stopped"];
|
||||
|
||||
[self setPosition:0];
|
||||
[self setSeekable:NO]; // the player stopped, disable the slider
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidStopNotficiation object:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidStopNotificiation object:nil];
|
||||
} else // paused
|
||||
{
|
||||
[[FIRCrashlytics crashlytics] log:@"Paused."];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidPauseNotficiation object:nil];
|
||||
//[SentrySDK captureMessage:@"Playback paused"];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidPauseNotificiation object:nil];
|
||||
}
|
||||
} else if(status == CogStatusPlaying) {
|
||||
[[FIRCrashlytics crashlytics] log:@"Started playing."];
|
||||
//[SentrySDK captureMessage:@"Playback started"];
|
||||
|
||||
if(!positionTimer) {
|
||||
positionTimer = [NSTimer timerWithTimeInterval:0.2 target:self selector:@selector(updatePosition:) userInfo:nil repeats:YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer:positionTimer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidResumeNotficiation object:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidResumeNotificiation object:nil];
|
||||
}
|
||||
|
||||
if(status == CogStatusStopped) {
|
||||
|
@ -684,7 +899,7 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
|
||||
- (void)audioPlayer:(AudioPlayer *)player didStopNaturally:(id)userInfo {
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"quitOnNaturalStop"]) {
|
||||
[[FIRCrashlytics crashlytics] log:@"Terminating due to natural stop."];
|
||||
//[SentrySDK captureMessage:@"Playback stopped naturally, terminating app"];
|
||||
[NSApp terminate:nil];
|
||||
}
|
||||
}
|
||||
|
@ -699,20 +914,20 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
- (void)audioPlayer:(AudioPlayer *)player restartPlaybackAtCurrentPosition:(id)userInfo {
|
||||
PlaylistEntry *pe = [playlistController currentEntry];
|
||||
BOOL paused = playbackStatus == CogStatusPaused;
|
||||
[[FIRCrashlytics crashlytics] logWithFormat:@"Restarting playback of track: %@", pe.url];
|
||||
[player play:pe.url withUserInfo:pe withRGInfo:makeRGInfo(pe) startPaused:paused andSeekTo:pe.seekable ? pe.currentPosition : 0.0];
|
||||
//[SentrySDK captureMessage:[NSString stringWithFormat:@"Playback restarting for track: %@", pe.url]];
|
||||
[player performSelectorOnMainThread:@selector(playBG:withUserInfo:withRGInfo:startPaused:andSeekTo:) withObjects:pe.url, pe, makeRGInfo(pe), @(paused), @(pe.seekable ? pe.currentPosition : 0.0), nil];
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player pushInfo:(NSDictionary *)info toTrack:(id)userInfo {
|
||||
PlaylistEntry *pe = (PlaylistEntry *)userInfo;
|
||||
if (!pe) pe = [playlistController currentEntry];
|
||||
if(!pe) pe = [playlistController currentEntry];
|
||||
[pe setMetadata:info];
|
||||
[playlistView refreshTrack:pe];
|
||||
// Delay the action until this function has returned to the audio thread
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 50 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
|
||||
self->playlistController.currentEntry = pe;
|
||||
[self sendMetaData];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidBeginNotficiation object:pe];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidBeginNotificiation object:pe];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -723,6 +938,15 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player updatePosition:(id)userInfo {
|
||||
if(userInfo) {
|
||||
PlaylistEntry *pe = (PlaylistEntry *)userInfo;
|
||||
if([pe current]) {
|
||||
[self updatePosition:userInfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player setError:(NSNumber *)status toTrack:(id)userInfo {
|
||||
PlaylistEntry *pe = (PlaylistEntry *)userInfo;
|
||||
[pe setError:[status boolValue]];
|
||||
|
@ -785,12 +1009,15 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
if(entry.album && [entry.album length])
|
||||
[songInfo setObject:entry.album forKey:MPMediaItemPropertyAlbumTitle];
|
||||
if(entry.albumArt) {
|
||||
CGSize artworkSize = CGSizeMake(500, 500);
|
||||
MPMediaItemArtwork *mpArtwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artworkSize
|
||||
requestHandler:^NSImage *_Nonnull(CGSize size) {
|
||||
return entry.albumArt;
|
||||
}];
|
||||
[songInfo setObject:mpArtwork forKey:MPMediaItemPropertyArtwork];
|
||||
// can't do && with @available
|
||||
if(@available(macOS 10.13.2, *)) {
|
||||
CGSize artworkSize = CGSizeMake(500, 500);
|
||||
MPMediaItemArtwork *mpArtwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artworkSize
|
||||
requestHandler:^NSImage *_Nonnull(CGSize size) {
|
||||
return entry.albumArt;
|
||||
}];
|
||||
[songInfo setObject:mpArtwork forKey:MPMediaItemPropertyArtwork];
|
||||
}
|
||||
}
|
||||
// I don't know what NPIC does with these since they aren't exposed in UI, but if we have them, use it.
|
||||
// There's a bunch of other metadata, but PlaylistEntry can't represent a lot of it.
|
||||
|
@ -799,8 +1026,10 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
if(entry.year) {
|
||||
// If PlaylistEntry can represent a full date like some tag formats can do, change it
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
NSDate *releaseYear = [calendar dateWithEra:1 year:entry.year month:0 day:0 hour:0 minute:0 second:0 nanosecond:0];
|
||||
[songInfo setObject:releaseYear forKey:MPMediaItemPropertyReleaseDate];
|
||||
NSDate *releaseYear = [calendar dateWithEra:1 year:entry.year month:1 day:1 hour:0 minute:0 second:0 nanosecond:0];
|
||||
if(releaseYear) {
|
||||
[songInfo setObject:releaseYear forKey:MPMediaItemPropertyReleaseDate];
|
||||
}
|
||||
}
|
||||
[songInfo setObject:@(entry.currentPosition) forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
|
||||
[songInfo setObject:entry.length forKey:MPMediaItemPropertyPlaybackDuration];
|
||||
|
|
|
@ -31,7 +31,7 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
|
|||
|
||||
PlaylistEntry *entry;
|
||||
|
||||
Boolean didGainUN;
|
||||
Boolean didGainUN API_AVAILABLE(macosx(10.14));
|
||||
}
|
||||
|
||||
- (void)initDefaults {
|
||||
|
@ -51,30 +51,32 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
|
|||
|
||||
didGainUN = NO;
|
||||
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center
|
||||
requestAuthorizationWithOptions:UNAuthorizationOptionAlert
|
||||
completionHandler:^(BOOL granted, NSError *_Nullable error) {
|
||||
self->didGainUN = granted;
|
||||
if(@available(macOS 10.14, *)) {
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center
|
||||
requestAuthorizationWithOptions:UNAuthorizationOptionAlert
|
||||
completionHandler:^(BOOL granted, NSError *_Nullable error) {
|
||||
self->didGainUN = granted;
|
||||
|
||||
if(granted) {
|
||||
UNNotificationAction *skipAction = [UNNotificationAction
|
||||
actionWithIdentifier:@"skip"
|
||||
title:@"Skip"
|
||||
options:UNNotificationActionOptionNone];
|
||||
if(granted) {
|
||||
UNNotificationAction *skipAction = [UNNotificationAction
|
||||
actionWithIdentifier:@"skip"
|
||||
title:@"Skip"
|
||||
options:UNNotificationActionOptionNone];
|
||||
|
||||
UNNotificationCategory *playCategory = [UNNotificationCategory
|
||||
categoryWithIdentifier:@"play"
|
||||
actions:@[skipAction]
|
||||
intentIdentifiers:@[]
|
||||
options:UNNotificationCategoryOptionNone];
|
||||
UNNotificationCategory *playCategory = [UNNotificationCategory
|
||||
categoryWithIdentifier:@"play"
|
||||
actions:@[skipAction]
|
||||
intentIdentifiers:@[]
|
||||
options:UNNotificationCategoryOptionNone];
|
||||
|
||||
[center setNotificationCategories:
|
||||
[NSSet setWithObject:playCategory]];
|
||||
}
|
||||
}];
|
||||
[center setNotificationCategories:
|
||||
[NSSet setWithObject:playCategory]];
|
||||
}
|
||||
}];
|
||||
|
||||
[center setDelegate:self];
|
||||
[center setDelegate:self];
|
||||
}
|
||||
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
[queue setMaxConcurrentOperationCount:1];
|
||||
|
@ -103,7 +105,6 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
if([[response actionIdentifier] isEqualToString:@"skip"]) {
|
||||
[playbackController next:self];
|
||||
}
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -159,14 +160,91 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
if([defaults boolForKey:@"notifications.enable"]) {
|
||||
if(didGainUN) {
|
||||
UNUserNotificationCenter *center =
|
||||
[UNUserNotificationCenter currentNotificationCenter];
|
||||
if(@available(macOS 10.14, *)) {
|
||||
if(didGainUN) {
|
||||
UNUserNotificationCenter *center =
|
||||
[UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
UNMutableNotificationContent *content =
|
||||
[[UNMutableNotificationContent alloc] init];
|
||||
UNMutableNotificationContent *content =
|
||||
[[UNMutableNotificationContent alloc] init];
|
||||
|
||||
content.title = @"Now Playing";
|
||||
content.title = @"Now Playing";
|
||||
|
||||
NSString *subtitle;
|
||||
NSString *artist = (pe.artist && [pe.artist length]) ? pe.artist : nil;
|
||||
NSString *album = (pe.album && [pe.album length]) ? pe.album : nil;
|
||||
if(artist && album) {
|
||||
subtitle = [NSString stringWithFormat:@"%@ - %@", artist, album];
|
||||
} else if(artist) {
|
||||
subtitle = artist;
|
||||
} else if(album) {
|
||||
subtitle = album;
|
||||
} else {
|
||||
subtitle = @"";
|
||||
}
|
||||
|
||||
NSString *body = [NSString stringWithFormat:@"%@\n%@", [pe title], subtitle];
|
||||
content.body = body;
|
||||
content.sound = nil;
|
||||
content.categoryIdentifier = @"play";
|
||||
|
||||
if([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
[pe albumArt]) {
|
||||
NSError *error = nil;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *tmpSubFolderURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()]
|
||||
URLByAppendingPathComponent:@"cog-artworks-cache"
|
||||
isDirectory:true];
|
||||
if([fileManager createDirectoryAtPath:[tmpSubFolderURL path]
|
||||
withIntermediateDirectories:true
|
||||
attributes:nil
|
||||
error:&error]) {
|
||||
NSString *tmpFileName =
|
||||
[[NSProcessInfo.processInfo globallyUniqueString]
|
||||
stringByAppendingString:@".jpg"];
|
||||
NSURL *fileURL =
|
||||
[tmpSubFolderURL URLByAppendingPathComponent:tmpFileName];
|
||||
NSImage *image = [pe albumArt];
|
||||
CGImageRef cgRef = [image CGImageForProposedRect:NULL
|
||||
context:nil
|
||||
hints:nil];
|
||||
|
||||
if(cgRef) {
|
||||
NSBitmapImageRep *newRep =
|
||||
[[NSBitmapImageRep alloc] initWithCGImage:cgRef];
|
||||
NSData *jpgData = [newRep
|
||||
representationUsingType:NSBitmapImageFileTypeJPEG
|
||||
properties:@{ NSImageCompressionFactor: @0.5f }];
|
||||
[jpgData writeToURL:fileURL atomically:YES];
|
||||
|
||||
UNNotificationAttachment *icon =
|
||||
[UNNotificationAttachment attachmentWithIdentifier:@"art"
|
||||
URL:fileURL
|
||||
options:nil
|
||||
error:&error];
|
||||
if(error) {
|
||||
// We have size limit of 10MB per image attachment.
|
||||
NSLog(@"%@", error.localizedDescription);
|
||||
} else {
|
||||
content.attachments = @[icon];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNNotificationRequest *request =
|
||||
[UNNotificationRequest requestWithIdentifier:@"PlayTrack"
|
||||
content:content
|
||||
trigger:nil];
|
||||
|
||||
[center addNotificationRequest:request
|
||||
withCompletionHandler:^(NSError *_Nullable error) {
|
||||
NSLog(@"%@", error.localizedDescription);
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
NSUserNotification *notif = [[NSUserNotification alloc] init];
|
||||
notif.title = [pe title];
|
||||
|
||||
NSString *subtitle;
|
||||
NSString *artist = (pe.artist && [pe.artist length]) ? pe.artist : nil;
|
||||
|
@ -181,64 +259,30 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
subtitle = @"";
|
||||
}
|
||||
|
||||
NSString *body = [NSString stringWithFormat:@"%@\n%@", [pe title], subtitle];
|
||||
content.body = body;
|
||||
content.sound = nil;
|
||||
content.categoryIdentifier = @"play";
|
||||
if([defaults boolForKey:@"notifications.itunes-style"]) {
|
||||
notif.subtitle = subtitle;
|
||||
[notif setValue:@YES forKey:@"_showsButtons"];
|
||||
} else {
|
||||
notif.informativeText = subtitle;
|
||||
}
|
||||
|
||||
if([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
[pe albumArt]) {
|
||||
NSError *error = nil;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *tmpSubFolderURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()]
|
||||
URLByAppendingPathComponent:@"cog-artworks-cache"
|
||||
isDirectory:true];
|
||||
if([fileManager createDirectoryAtPath:[tmpSubFolderURL path]
|
||||
withIntermediateDirectories:true
|
||||
attributes:nil
|
||||
error:&error]) {
|
||||
NSString *tmpFileName =
|
||||
[[NSProcessInfo.processInfo globallyUniqueString]
|
||||
stringByAppendingString:@".jpg"];
|
||||
NSURL *fileURL =
|
||||
[tmpSubFolderURL URLByAppendingPathComponent:tmpFileName];
|
||||
if([notif respondsToSelector:@selector(setContentImage:)]) {
|
||||
if([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
[pe albumArtInternal]) {
|
||||
NSImage *image = [pe albumArt];
|
||||
CGImageRef cgRef = [image CGImageForProposedRect:NULL
|
||||
context:nil
|
||||
hints:nil];
|
||||
|
||||
if(cgRef) {
|
||||
NSBitmapImageRep *newRep =
|
||||
[[NSBitmapImageRep alloc] initWithCGImage:cgRef];
|
||||
NSData *jpgData = [newRep
|
||||
representationUsingType:NSBitmapImageFileTypeJPEG
|
||||
properties:@{ NSImageCompressionFactor: @0.5f }];
|
||||
[jpgData writeToURL:fileURL atomically:YES];
|
||||
|
||||
UNNotificationAttachment *icon =
|
||||
[UNNotificationAttachment attachmentWithIdentifier:@"art"
|
||||
URL:fileURL
|
||||
options:nil
|
||||
error:&error];
|
||||
if(error) {
|
||||
// We have size limit of 10MB per image attachment.
|
||||
NSLog(@"%@", error.localizedDescription);
|
||||
} else {
|
||||
content.attachments = @[icon];
|
||||
}
|
||||
if([defaults boolForKey:@"notifications.itunes-style"]) {
|
||||
[notif setValue:image forKey:@"_identityImage"];
|
||||
} else {
|
||||
notif.contentImage = image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNNotificationRequest *request =
|
||||
[UNNotificationRequest requestWithIdentifier:@"PlayTrack"
|
||||
content:content
|
||||
trigger:nil];
|
||||
notif.actionButtonTitle = NSLocalizedString(@"SkipAction", @"");
|
||||
|
||||
[center addNotificationRequest:request
|
||||
withCompletionHandler:^(NSError *_Nullable error) {
|
||||
NSLog(@"%@", error.localizedDescription);
|
||||
}];
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter]
|
||||
scheduleNotification:notif];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,19 +322,19 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
- (void)awakeFromNib {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(playbackDidBegin:)
|
||||
name:CogPlaybackDidBeginNotficiation
|
||||
name:CogPlaybackDidBeginNotificiation
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(playbackDidPause:)
|
||||
name:CogPlaybackDidPauseNotficiation
|
||||
name:CogPlaybackDidPauseNotificiation
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(playbackDidResume:)
|
||||
name:CogPlaybackDidResumeNotficiation
|
||||
name:CogPlaybackDidResumeNotificiation
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(playbackDidStop:)
|
||||
name:CogPlaybackDidStopNotficiation
|
||||
name:CogPlaybackDidStopNotificiation
|
||||
object:nil];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Plugin.h"
|
||||
#import <CogAudio/Plugin.h>
|
||||
|
||||
@interface AudioDecoder : NSObject {
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
OutputNode *output;
|
||||
|
||||
double volume;
|
||||
double pitch;
|
||||
double tempo;
|
||||
|
||||
NSMutableArray *chainQueue;
|
||||
|
||||
|
@ -61,12 +63,14 @@
|
|||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused andSeekTo:(double)time;
|
||||
- (void)playBG:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(NSNumber *)paused andSeekTo:(NSNumber *)time;
|
||||
|
||||
- (void)stop;
|
||||
- (void)pause;
|
||||
- (void)resume;
|
||||
|
||||
- (void)seekToTime:(double)time;
|
||||
- (void)seekToTimeBG:(NSNumber *)time;
|
||||
- (void)setVolume:(double)v;
|
||||
- (double)volume;
|
||||
- (double)volumeUp:(double)amount;
|
||||
|
@ -134,5 +138,6 @@
|
|||
- (void)audioPlayer:(AudioPlayer *)player restartPlaybackAtCurrentPosition:(id)userInfo;
|
||||
- (void)audioPlayer:(AudioPlayer *)player pushInfo:(NSDictionary *)info toTrack:(id)userInfo;
|
||||
- (void)audioPlayer:(AudioPlayer *)player reportPlayCountForTrack:(id)userInfo;
|
||||
- (void)audioPlayer:(AudioPlayer *)player updatePosition:(id)userInfo;
|
||||
- (void)audioPlayer:(AudioPlayer *)player setError:(NSNumber *)status toTrack:(id)userInfo;
|
||||
@end
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
bufferChain = nil;
|
||||
outputLaunched = NO;
|
||||
endOfInputReached = NO;
|
||||
|
||||
// Safety
|
||||
pitch = 1.0;
|
||||
tempo = 1.0;
|
||||
|
||||
chainQueue = [[NSMutableArray alloc] init];
|
||||
|
||||
|
@ -56,18 +60,29 @@
|
|||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:paused andSeekTo:0.0];
|
||||
}
|
||||
|
||||
- (void)playBG:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(NSNumber *)paused andSeekTo:(NSNumber *)time {
|
||||
@synchronized (self) {
|
||||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:[paused boolValue] andSeekTo:[time doubleValue]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused andSeekTo:(double)time {
|
||||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:paused andSeekTo:time andResumeInterval:NO];
|
||||
}
|
||||
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused andSeekTo:(double)time andResumeInterval:(BOOL)resumeInterval {
|
||||
ALog(@"Opening file for playback: %@ at seek offset %f%@", url, time, (paused) ? @", starting paused" : @"");
|
||||
|
||||
[self waitUntilCallbacksExit];
|
||||
if(output) {
|
||||
[output setShouldContinue:NO];
|
||||
[output close];
|
||||
[output fadeOutBackground];
|
||||
}
|
||||
if(!output) {
|
||||
output = [[OutputNode alloc] initWithController:self previous:nil];
|
||||
if(![output setupWithInterval:resumeInterval]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
[output setup];
|
||||
[output setVolume:volume];
|
||||
@synchronized(chainQueue) {
|
||||
for(id anObject in chainQueue) {
|
||||
|
@ -83,9 +98,11 @@
|
|||
}
|
||||
|
||||
bufferChain = [[BufferChain alloc] initWithController:self];
|
||||
[self notifyStreamChanged:userInfo];
|
||||
if(!resumeInterval) {
|
||||
[self notifyStreamChanged:userInfo];
|
||||
}
|
||||
|
||||
while(![bufferChain open:url withUserInfo:userInfo withRGInfo:rgi]) {
|
||||
while(![bufferChain open:url withOutputFormat:[output format] withUserInfo:userInfo withRGInfo:rgi]) {
|
||||
bufferChain = nil;
|
||||
|
||||
[self requestNextStream:userInfo];
|
||||
|
@ -114,15 +131,23 @@
|
|||
|
||||
[self setShouldContinue:YES];
|
||||
|
||||
outputLaunched = NO;
|
||||
if(!resumeInterval) {
|
||||
outputLaunched = NO;
|
||||
}
|
||||
startedPaused = paused;
|
||||
initialBufferFilled = NO;
|
||||
previousUserInfo = userInfo;
|
||||
|
||||
[bufferChain launchThreads];
|
||||
|
||||
if(paused)
|
||||
if(paused) {
|
||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||
if(time > 0.0) {
|
||||
[self updatePosition:userInfo];
|
||||
}
|
||||
} else if(resumeInterval) {
|
||||
[output fadeIn];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
|
@ -148,7 +173,7 @@
|
|||
}
|
||||
|
||||
- (void)pause {
|
||||
[output pause];
|
||||
[output fadeOut];
|
||||
|
||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||
}
|
||||
|
@ -160,33 +185,38 @@
|
|||
[self launchOutputThread];
|
||||
}
|
||||
|
||||
[output fadeIn];
|
||||
[output resume];
|
||||
|
||||
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)seekToTimeBG:(NSNumber *)time {
|
||||
[self seekToTime:[time doubleValue]];
|
||||
}
|
||||
|
||||
- (void)seekToTime:(double)time {
|
||||
if(endOfInputReached) {
|
||||
// This is a dirty hack in case the playback has finished with the track
|
||||
// that the user thinks they're seeking into
|
||||
CogStatus status = (CogStatus)currentPlaybackStatus;
|
||||
NSURL *url;
|
||||
id userInfo;
|
||||
NSDictionary *rgi;
|
||||
[output fadeOutBackground];
|
||||
[output setVolume:volume];
|
||||
|
||||
@synchronized(chainQueue) {
|
||||
url = [bufferChain streamURL];
|
||||
userInfo = [bufferChain userInfo];
|
||||
rgi = [bufferChain rgInfo];
|
||||
[output seek:time];
|
||||
[bufferChain seek:time];
|
||||
|
||||
CogStatus status = (CogStatus)currentPlaybackStatus;
|
||||
BOOL paused = status == CogStatusPaused;
|
||||
id userInfo;
|
||||
|
||||
@synchronized(chainQueue) {
|
||||
userInfo = [bufferChain userInfo];
|
||||
}
|
||||
|
||||
if(paused) {
|
||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||
if(time > 0.0) {
|
||||
[self updatePosition:userInfo];
|
||||
}
|
||||
|
||||
[self stop];
|
||||
|
||||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:(status == CogStatusPaused) andSeekTo:time];
|
||||
} else {
|
||||
// Still decoding the current file, safe to seek within it
|
||||
[output seek:time];
|
||||
[bufferChain seek:time];
|
||||
[output fadeIn];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,6 +263,10 @@
|
|||
[self sendDelegateMethod:@selector(audioPlayer:restartPlaybackAtCurrentPosition:) withObject:previousUserInfo waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)updatePosition:(id)userInfo {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:updatePosition:) withObject:userInfo waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)pushInfo:(NSDictionary *)info toTrack:(id)userInfo {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:pushInfo:toTrack:) withObject:info withObject:userInfo waitUntilDone:NO];
|
||||
}
|
||||
|
@ -388,10 +422,31 @@
|
|||
|
||||
if([unixPathNext isEqualToString:unixPathPrev])
|
||||
pathsEqual = YES;
|
||||
} else if(![nextStream isFileURL] && ![[lastChain streamURL] isFileURL]) {
|
||||
@try {
|
||||
NSURL *lastURL = [lastChain streamURL];
|
||||
NSString *nextScheme = [nextStream scheme];
|
||||
NSString *lastScheme = [lastURL scheme];
|
||||
NSString *nextHost = [nextStream host];
|
||||
NSString *lastHost = [lastURL host];
|
||||
NSString *nextPath = [nextStream path];
|
||||
NSString *lastPath = [lastURL path];
|
||||
if(nextScheme && lastScheme && [nextScheme isEqualToString:lastScheme]) {
|
||||
if((!nextHost && !lastHost) ||
|
||||
(nextHost && lastHost && [nextHost isEqualToString:lastHost])) {
|
||||
if(nextPath && lastPath && [nextPath isEqualToString:lastPath]) {
|
||||
pathsEqual = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@catch(NSException *e) {
|
||||
DLog(@"Exception thrown checking file match: %@", e);
|
||||
}
|
||||
}
|
||||
|
||||
if(pathsEqual || ([[nextStream scheme] isEqualToString:[[lastChain streamURL] scheme]] && (([nextStream host] == nil && [[lastChain streamURL] host] == nil) || [[nextStream host] isEqualToString:[[lastChain streamURL] host]]) && [[nextStream path] isEqualToString:[[lastChain streamURL] path]])) {
|
||||
if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
|
||||
if(pathsEqual) {
|
||||
if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
|
||||
[newChain setStreamURL:nextStream];
|
||||
|
||||
@synchronized(chainQueue) {
|
||||
|
@ -410,7 +465,7 @@
|
|||
|
||||
NSURL *url = nextStream;
|
||||
|
||||
while(shouldContinue && ![newChain open:url withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
|
||||
while(shouldContinue && ![newChain open:url withOutputFormat:[output format] withUserInfo:nextStreamUserInfo withRGInfo:nextStreamRGInfo]) {
|
||||
if(nextStream == nil) {
|
||||
newChain = nil;
|
||||
if(output)
|
||||
|
@ -475,6 +530,7 @@
|
|||
break;
|
||||
}
|
||||
|
||||
[bufferChain setShouldContinue:NO];
|
||||
bufferChain = nil;
|
||||
bufferChain = [chainQueue objectAtIndex:0];
|
||||
|
||||
|
@ -648,7 +704,7 @@
|
|||
while(atomic_load_explicit(&refCount, memory_order_relaxed) != 0) {
|
||||
[semaphore signal]; // Gotta poke this periodically
|
||||
if(mainThread)
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
|
||||
else
|
||||
usleep(500);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,8 @@ enum {
|
|||
AudioStreamBasicDescription format;
|
||||
NSMutableData *chunkData;
|
||||
uint32_t channelConfig;
|
||||
double streamTimestamp;
|
||||
double streamTimeRatio;
|
||||
BOOL formatAssigned;
|
||||
BOOL lossless;
|
||||
BOOL hdcd;
|
||||
|
@ -72,6 +74,8 @@ enum {
|
|||
|
||||
@property AudioStreamBasicDescription format;
|
||||
@property uint32_t channelConfig;
|
||||
@property double streamTimestamp;
|
||||
@property double streamTimeRatio;
|
||||
@property BOOL lossless;
|
||||
|
||||
+ (uint32_t)guessChannelConfig:(uint32_t)channelCount;
|
||||
|
@ -83,7 +87,7 @@ enum {
|
|||
- (id)init;
|
||||
- (id)initWithProperties:(NSDictionary *)properties;
|
||||
|
||||
- (void)assignSamples:(const void *)data frameCount:(size_t)count;
|
||||
- (void)assignSamples:(const void *_Nonnull)data frameCount:(size_t)count;
|
||||
- (void)assignData:(NSData *)data;
|
||||
|
||||
- (NSData *)removeSamples:(size_t)frameCount;
|
||||
|
@ -94,10 +98,13 @@ enum {
|
|||
- (void)setFrameCount:(size_t)count; // For truncation only
|
||||
|
||||
- (double)duration;
|
||||
- (double)durationRatioed;
|
||||
|
||||
- (BOOL)isHDCD;
|
||||
- (void)setHDCD;
|
||||
|
||||
- (AudioChunk *)copy;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
formatAssigned = NO;
|
||||
lossless = NO;
|
||||
hdcd = NO;
|
||||
streamTimestamp = 0.0;
|
||||
streamTimeRatio = 1.0;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -31,11 +33,25 @@
|
|||
chunkData = [[NSMutableData alloc] init];
|
||||
[self setFormat:propertiesToASBD(properties)];
|
||||
lossless = [[properties objectForKey:@"encoding"] isEqualToString:@"lossless"];
|
||||
hdcd = NO;
|
||||
streamTimestamp = 0.0;
|
||||
streamTimeRatio = 1.0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (AudioChunk *)copy {
|
||||
AudioChunk *outputChunk = [[AudioChunk alloc] init];
|
||||
[outputChunk setFormat:format];
|
||||
[outputChunk setChannelConfig:channelConfig];
|
||||
if(hdcd) [outputChunk setHDCD];
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:streamTimeRatio];
|
||||
[outputChunk assignData:chunkData];
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
static const uint32_t AudioChannelConfigTable[] = {
|
||||
0,
|
||||
AudioConfigMono,
|
||||
|
@ -117,6 +133,8 @@ static const uint32_t AudioChannelConfigTable[] = {
|
|||
}
|
||||
|
||||
@synthesize lossless;
|
||||
@synthesize streamTimestamp;
|
||||
@synthesize streamTimeRatio;
|
||||
|
||||
- (AudioStreamBasicDescription)format {
|
||||
return format;
|
||||
|
@ -141,7 +159,7 @@ static const uint32_t AudioChannelConfigTable[] = {
|
|||
channelConfig = config;
|
||||
}
|
||||
|
||||
- (void)assignSamples:(const void *)data frameCount:(size_t)count {
|
||||
- (void)assignSamples:(const void *_Nonnull)data frameCount:(size_t)count {
|
||||
if(formatAssigned) {
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
[chunkData appendBytes:data length:bytesPerPacket * count];
|
||||
|
@ -155,10 +173,13 @@ static const uint32_t AudioChannelConfigTable[] = {
|
|||
- (NSData *)removeSamples:(size_t)frameCount {
|
||||
if(formatAssigned) {
|
||||
@autoreleasepool {
|
||||
const double secondsDuration = (double)(frameCount) / format.mSampleRate;
|
||||
const double DSDrate = (format.mBitsPerChannel == 1) ? 8.0 : 1.0;
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
const size_t byteCount = bytesPerPacket * frameCount;
|
||||
NSData *ret = [chunkData subdataWithRange:NSMakeRange(0, byteCount)];
|
||||
[chunkData replaceBytesInRange:NSMakeRange(0, byteCount) withBytes:NULL length:0];
|
||||
streamTimestamp += secondsDuration * streamTimeRatio * DSDrate;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -188,14 +209,19 @@ static const uint32_t AudioChannelConfigTable[] = {
|
|||
}
|
||||
|
||||
- (double)duration {
|
||||
if(formatAssigned) {
|
||||
if(formatAssigned && [chunkData length]) {
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
const double sampleRate = format.mSampleRate;
|
||||
return (double)([chunkData length] / bytesPerPacket) / sampleRate;
|
||||
const double DSDrate = (format.mBitsPerChannel == 1) ? 8.0 : 1.0;
|
||||
return ((double)([chunkData length] / bytesPerPacket) / sampleRate) * DSDrate;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
- (double)durationRatioed {
|
||||
return [self duration] * streamTimeRatio;
|
||||
}
|
||||
|
||||
- (BOOL)isHDCD {
|
||||
return hdcd;
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "AudioPlayer.h"
|
||||
#import "ConverterNode.h"
|
||||
#import "InputNode.h"
|
||||
#import <CogAudio/AudioPlayer.h>
|
||||
#import <CogAudio/ConverterNode.h>
|
||||
#import <CogAudio/InputNode.h>
|
||||
|
||||
@interface BufferChain : NSObject {
|
||||
InputNode *inputNode;
|
||||
|
@ -26,15 +26,16 @@
|
|||
}
|
||||
|
||||
- (id)initWithController:(id)c;
|
||||
- (void)buildChain;
|
||||
- (BOOL)buildChain;
|
||||
|
||||
- (BOOL)open:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
// Used when changing tracks to reuse the same decoder
|
||||
- (BOOL)openWithInput:(InputNode *)i withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
// Used when resetting the decoder on seek
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
|
||||
withOutputFormat:(AudioStreamBasicDescription)outputFormat
|
||||
withUserInfo:(id)userInfo
|
||||
withRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
#import "BufferChain.h"
|
||||
#import "AudioSource.h"
|
||||
#import "CoreAudioUtils.h"
|
||||
#import "DSPDownmixNode.h"
|
||||
#import "OutputNode.h"
|
||||
|
||||
#import "AudioPlayer.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation BufferChain
|
||||
|
@ -30,21 +33,36 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)buildChain {
|
||||
inputNode = nil;
|
||||
- (BOOL)buildChain {
|
||||
// Cut off output source
|
||||
finalNode = nil;
|
||||
|
||||
// Tear them down in reverse
|
||||
converterNode = nil;
|
||||
inputNode = nil;
|
||||
|
||||
inputNode = [[InputNode alloc] initWithController:self previous:nil];
|
||||
if(!inputNode) return NO;
|
||||
converterNode = [[ConverterNode alloc] initWithController:self previous:inputNode];
|
||||
if(!converterNode) return NO;
|
||||
|
||||
finalNode = converterNode;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
|
||||
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
|
||||
if(!url) {
|
||||
DLog(@"Player attempted to play invalid file...");
|
||||
return NO;
|
||||
}
|
||||
[self setStreamURL:url];
|
||||
[self setUserInfo:userInfo];
|
||||
|
||||
[self buildChain];
|
||||
if(![self buildChain]) {
|
||||
DLog(@"Couldn't build processing chain...");
|
||||
return NO;
|
||||
}
|
||||
|
||||
id<CogSource> source = [AudioSource audioSourceForURL:url];
|
||||
DLog(@"Opening: %@", url);
|
||||
|
@ -59,15 +77,9 @@
|
|||
if(![inputNode openWithSource:source])
|
||||
return NO;
|
||||
|
||||
NSDictionary *properties = [inputNode properties];
|
||||
|
||||
AudioStreamBasicDescription inputFormat = [inputNode nodeFormat];
|
||||
uint32_t inputChannelConfig = 0;
|
||||
if([properties valueForKey:@"channelConfig"])
|
||||
inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];
|
||||
|
||||
if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
if(![self initConverter:outputFormat])
|
||||
return NO;
|
||||
[self initDownmixer];
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
||||
|
@ -76,24 +88,20 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)openWithInput:(InputNode *)i withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
|
||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
|
||||
DLog(@"New buffer chain!");
|
||||
[self setUserInfo:userInfo];
|
||||
[self buildChain];
|
||||
if(![self buildChain]) {
|
||||
DLog(@"Couldn't build processing chain...");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if(![inputNode openWithDecoder:[i decoder]])
|
||||
return NO;
|
||||
|
||||
NSDictionary *properties = [inputNode properties];
|
||||
|
||||
AudioStreamBasicDescription inputFormat = [inputNode nodeFormat];
|
||||
uint32_t inputChannelConfig = 0;
|
||||
if([properties valueForKey:@"channelConfig"])
|
||||
inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];
|
||||
|
||||
DLog(@"Input Properties: %@", properties);
|
||||
if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
if(![self initConverter:outputFormat])
|
||||
return NO;
|
||||
[self initDownmixer];
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
||||
|
@ -101,16 +109,30 @@
|
|||
}
|
||||
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
|
||||
withOutputFormat:(AudioStreamBasicDescription)outputFormat
|
||||
withUserInfo:(id)userInfo
|
||||
withRGInfo:(NSDictionary *)rgi;
|
||||
{
|
||||
DLog(@"New buffer chain!");
|
||||
[self setUserInfo:userInfo];
|
||||
[self buildChain];
|
||||
if(![self buildChain]) {
|
||||
DLog(@"Couldn't build processing chain...");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if(![inputNode openWithDecoder:decoder])
|
||||
return NO;
|
||||
|
||||
if(![self initConverter:outputFormat])
|
||||
return NO;
|
||||
[self initDownmixer];
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)initConverter:(AudioStreamBasicDescription)outputFormat {
|
||||
NSDictionary *properties = [inputNode properties];
|
||||
|
||||
DLog(@"Input Properties: %@", properties);
|
||||
|
@ -120,14 +142,23 @@
|
|||
if([properties valueForKey:@"channelConfig"])
|
||||
inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];
|
||||
|
||||
if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
|
||||
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
|
||||
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
|
||||
|
||||
if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
return NO;
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)initDownmixer {
|
||||
AudioPlayer * audioPlayer = controller;
|
||||
OutputNode *outputNode = [audioPlayer output];
|
||||
DSPDownmixNode *downmixNode = [outputNode downmix];
|
||||
[downmixNode setOutputFormat:[outputNode deviceFormat] withChannelConfig:[outputNode deviceChannelConfig]];
|
||||
}
|
||||
|
||||
- (void)launchThreads {
|
||||
DLog(@"Properties: %@", [inputNode properties]);
|
||||
|
||||
|
@ -155,7 +186,7 @@
|
|||
- (void)dealloc {
|
||||
[inputNode setShouldContinue:NO];
|
||||
[[inputNode exitAtTheEndOfTheStream] signal];
|
||||
[[inputNode semaphore] signal];
|
||||
[[inputNode writeSemaphore] signal];
|
||||
if(![inputNode threadExited])
|
||||
[[inputNode exitAtTheEndOfTheStream] wait]; // wait for decoder to be closed (see InputNode's -(void)process )
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#import <CoreAudio/CoreAudio.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AudioChunk.h"
|
||||
#import <CogAudio/AudioChunk.h>
|
||||
|
||||
#import <CogAudio/CogSemaphore.h>
|
||||
|
||||
|
@ -19,11 +19,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@interface ChunkList : NSObject {
|
||||
NSMutableArray<AudioChunk *> *chunkList;
|
||||
double listDuration;
|
||||
double listDurationRatioed;
|
||||
double maxDuration;
|
||||
|
||||
BOOL inAdder;
|
||||
BOOL inRemover;
|
||||
BOOL inPeeker;
|
||||
BOOL inMerger;
|
||||
BOOL inConverter;
|
||||
BOOL stopping;
|
||||
|
||||
// For format converter
|
||||
|
@ -36,6 +39,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
int dsd2pcmLatency;
|
||||
#endif
|
||||
|
||||
BOOL observersRegistered;
|
||||
BOOL halveDSDVolume;
|
||||
|
||||
BOOL enableHDCD;
|
||||
void *hdcd_decoder;
|
||||
|
||||
BOOL formatRead;
|
||||
|
@ -51,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
@property(readonly) double listDuration;
|
||||
@property(readonly) double listDurationRatioed;
|
||||
@property(readonly) double maxDuration;
|
||||
|
||||
- (id)initWithMaximumDuration:(double)duration;
|
||||
|
@ -67,6 +75,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config;
|
||||
|
||||
- (BOOL)peekTimestamp:(nonnull double *)timestamp timeRatio:(nonnull double *)timeRatio;
|
||||
|
||||
// Helpers
|
||||
- (AudioChunk *)removeAndMergeSamples:(size_t)maxFrameCount callBlock:(BOOL(NS_NOESCAPE ^ _Nonnull)(void))block;
|
||||
- (AudioChunk *)removeAndMergeSamplesAsFloat32:(size_t)maxFrameCount callBlock:(BOOL(NS_NOESCAPE ^ _Nonnull)(void))block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#import "BadSampleCleaner.h"
|
||||
#endif
|
||||
|
||||
static void *kChunkListContext = &kChunkListContext;
|
||||
|
||||
#if DSD_DECIMATE
|
||||
/**
|
||||
* DSD 2 PCM: Stage 1:
|
||||
|
@ -28,6 +30,14 @@
|
|||
* @author Sebastian Gesemann
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the 2nd half of an even order symmetric FIR
|
||||
* lowpass filter (to be used on a signal sampled at 44100*64 Hz)
|
||||
* Passband is 0-24 kHz (ripples +/- 0.025 dB)
|
||||
* Stopband starts at 176.4 kHz (rejection: 170 dB)
|
||||
* The overall gain is 2.0
|
||||
*/
|
||||
|
||||
#define dsd2pcm_FILTER_COEFFS_COUNT 64
|
||||
static const float dsd2pcm_FILTER_COEFFS[64] = {
|
||||
0.09712411121659f, 0.09613438994044f, 0.09417884216316f, 0.09130441727307f,
|
||||
|
@ -209,7 +219,7 @@ static void dsd2pcm_reset(void *_state) {
|
|||
static int dsd2pcm_latency(void *_state) {
|
||||
struct dsd2pcm_state *state = (struct dsd2pcm_state *)_state;
|
||||
if(state)
|
||||
return state->FIFO_LENGTH;
|
||||
return state->FILT_LOOKUP_PARTS * 8;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
@ -359,6 +369,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
@implementation ChunkList
|
||||
|
||||
@synthesize listDuration;
|
||||
@synthesize listDurationRatioed;
|
||||
@synthesize maxDuration;
|
||||
|
||||
- (id)initWithMaximumDuration:(double)duration {
|
||||
|
@ -367,11 +378,14 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
if(self) {
|
||||
chunkList = [[NSMutableArray alloc] init];
|
||||
listDuration = 0.0;
|
||||
listDurationRatioed = 0.0;
|
||||
maxDuration = duration;
|
||||
|
||||
inAdder = NO;
|
||||
inRemover = NO;
|
||||
inPeeker = NO;
|
||||
inMerger = NO;
|
||||
inConverter = NO;
|
||||
stopping = NO;
|
||||
|
||||
formatRead = NO;
|
||||
|
@ -384,16 +398,40 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
dsd2pcmCount = 0;
|
||||
dsd2pcmLatency = 0;
|
||||
#endif
|
||||
|
||||
observersRegistered = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addObservers {
|
||||
if(!observersRegistered) {
|
||||
halveDSDVolume = NO;
|
||||
enableHDCD = NO;
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.halveDSDVolume" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kChunkListContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHDCD" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kChunkListContext];
|
||||
|
||||
observersRegistered = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObservers {
|
||||
if(observersRegistered) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.halveDSDVolume" context:kChunkListContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHDCD" context:kChunkListContext];
|
||||
|
||||
observersRegistered = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
stopping = YES;
|
||||
while(inAdder || inRemover || inPeeker) {
|
||||
while(inAdder || inRemover || inPeeker || inMerger || inConverter) {
|
||||
usleep(500);
|
||||
}
|
||||
[self removeObservers];
|
||||
if(hdcd_decoder) {
|
||||
free(hdcd_decoder);
|
||||
hdcd_decoder = NULL;
|
||||
|
@ -413,10 +451,24 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if(context != kChunkListContext) {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
return;
|
||||
}
|
||||
|
||||
if([keyPath isEqualToString:@"values.halveDSDVolume"]) {
|
||||
halveDSDVolume = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"halveDSDVolume"];
|
||||
} else if([keyPath isEqualToString:@"values.enableHDCD"]) {
|
||||
enableHDCD = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"enableHDCD"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
@synchronized(chunkList) {
|
||||
[chunkList removeAllObjects];
|
||||
listDuration = 0.0;
|
||||
listDurationRatioed = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,7 +479,9 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
}
|
||||
|
||||
- (BOOL)isFull {
|
||||
return (maxDuration - listDuration) < 0.05;
|
||||
@synchronized (chunkList) {
|
||||
return (maxDuration - listDuration) < 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addChunk:(AudioChunk *)chunk {
|
||||
|
@ -436,10 +490,12 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
inAdder = YES;
|
||||
|
||||
const double chunkDuration = [chunk duration];
|
||||
const double chunkDurationRatioed = [chunk durationRatioed];
|
||||
|
||||
@synchronized(chunkList) {
|
||||
[chunkList addObject:chunk];
|
||||
listDuration += chunkDuration;
|
||||
listDurationRatioed += chunkDurationRatioed;
|
||||
}
|
||||
|
||||
inAdder = NO;
|
||||
|
@ -460,15 +516,21 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
if([chunk frameCount] <= maxFrameCount) {
|
||||
[chunkList removeObjectAtIndex:0];
|
||||
listDuration -= [chunk duration];
|
||||
listDurationRatioed -= [chunk durationRatioed];
|
||||
inRemover = NO;
|
||||
return chunk;
|
||||
}
|
||||
double streamTimestamp = [chunk streamTimestamp];
|
||||
NSData *removedData = [chunk removeSamples:maxFrameCount];
|
||||
AudioChunk *ret = [[AudioChunk alloc] init];
|
||||
[ret setFormat:[chunk format]];
|
||||
[ret setChannelConfig:[chunk channelConfig]];
|
||||
[ret setLossless:[chunk lossless]];
|
||||
[ret setStreamTimestamp:streamTimestamp];
|
||||
[ret setStreamTimeRatio:[chunk streamTimeRatio]];
|
||||
[ret assignData:removedData];
|
||||
listDuration -= [ret duration];
|
||||
listDurationRatioed -= [ret durationRatioed];
|
||||
inRemover = NO;
|
||||
return ret;
|
||||
}
|
||||
|
@ -478,7 +540,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
if(stopping) {
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
|
||||
@synchronized (chunkList) {
|
||||
inRemover = YES;
|
||||
if(![chunkList count]) {
|
||||
|
@ -495,22 +557,126 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
if([chunk frameCount] <= maxFrameCount) {
|
||||
[chunkList removeObjectAtIndex:0];
|
||||
listDuration -= [chunk duration];
|
||||
listDurationRatioed -= [chunk durationRatioed];
|
||||
inRemover = NO;
|
||||
return [self convertChunk:chunk];
|
||||
}
|
||||
double streamTimestamp = [chunk streamTimestamp];
|
||||
NSData *removedData = [chunk removeSamples:maxFrameCount];
|
||||
AudioChunk *ret = [[AudioChunk alloc] init];
|
||||
[ret setFormat:[chunk format]];
|
||||
[ret setChannelConfig:[chunk channelConfig]];
|
||||
[ret setLossless:[chunk lossless]];
|
||||
[ret setStreamTimestamp:streamTimestamp];
|
||||
[ret setStreamTimeRatio:[chunk streamTimeRatio]];
|
||||
[ret assignData:removedData];
|
||||
listDuration -= [ret duration];
|
||||
listDurationRatioed -= [ret durationRatioed];
|
||||
inRemover = NO;
|
||||
return [self convertChunk:ret];
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)removeAndMergeSamples:(size_t)maxFrameCount callBlock:(BOOL(NS_NOESCAPE ^ _Nonnull)(void))block {
|
||||
if(stopping) {
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
inMerger = YES;
|
||||
|
||||
BOOL formatSet = NO;
|
||||
AudioStreamBasicDescription currentFormat;
|
||||
uint32_t currentChannelConfig = 0;
|
||||
|
||||
double streamTimestamp = 0.0;
|
||||
double streamTimeRatio = 1.0;
|
||||
BOOL blocked = NO;
|
||||
while(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) {
|
||||
if((blocked = block())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(blocked) {
|
||||
inMerger = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
AudioChunk *chunk;
|
||||
size_t totalFrameCount = 0;
|
||||
AudioChunk *outputChunk = [[AudioChunk alloc] init];
|
||||
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:streamTimeRatio];
|
||||
|
||||
while(!stopping && totalFrameCount < maxFrameCount) {
|
||||
AudioStreamBasicDescription newFormat;
|
||||
uint32_t newChannelConfig;
|
||||
if(![self peekFormat:&newFormat channelConfig:&newChannelConfig]) {
|
||||
if(block()) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(formatSet &&
|
||||
(memcmp(&newFormat, ¤tFormat, sizeof(newFormat)) != 0 ||
|
||||
newChannelConfig != currentChannelConfig)) {
|
||||
break;
|
||||
} else if(!formatSet) {
|
||||
[outputChunk setFormat:newFormat];
|
||||
[outputChunk setChannelConfig:newChannelConfig];
|
||||
currentFormat = newFormat;
|
||||
currentChannelConfig = newChannelConfig;
|
||||
formatSet = YES;
|
||||
}
|
||||
|
||||
chunk = [self removeSamples:maxFrameCount - totalFrameCount];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if(block()) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if([chunk isHDCD]) {
|
||||
[outputChunk setHDCD];
|
||||
}
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
[outputChunk assignData:sampleData];
|
||||
|
||||
totalFrameCount += frameCount;
|
||||
}
|
||||
|
||||
if(!totalFrameCount) {
|
||||
inMerger = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
inMerger = NO;
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
- (AudioChunk *)removeAndMergeSamplesAsFloat32:(size_t)maxFrameCount callBlock:(BOOL(NS_NOESCAPE ^ _Nonnull)(void))block {
|
||||
AudioChunk *ret = [self removeAndMergeSamples:maxFrameCount callBlock:block];
|
||||
return [self convertChunk:ret];
|
||||
}
|
||||
|
||||
- (AudioChunk *)convertChunk:(AudioChunk *)inChunk {
|
||||
if(stopping) return [[AudioChunk alloc] init];
|
||||
|
||||
inConverter = YES;
|
||||
|
||||
AudioStreamBasicDescription chunkFormat = [inChunk format];
|
||||
if(![inChunk frameCount] ||
|
||||
(chunkFormat.mFormatFlags == kAudioFormatFlagsNativeFloatPacked &&
|
||||
chunkFormat.mBitsPerChannel == 32)) {
|
||||
inConverter = NO;
|
||||
return inChunk;
|
||||
}
|
||||
|
||||
uint32_t chunkConfig = [inChunk channelConfig];
|
||||
BOOL chunkLossless = [inChunk lossless];
|
||||
if(!formatRead || memcmp(&chunkFormat, &inputFormat, sizeof(chunkFormat)) != 0 ||
|
||||
|
@ -521,8 +687,10 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
inputLossless = chunkLossless;
|
||||
|
||||
BOOL isFloat = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsFloat);
|
||||
if((!isFloat && !(inputFormat.mBitsPerChannel >= 1 && inputFormat.mBitsPerChannel <= 32)) || (isFloat && !(inputFormat.mBitsPerChannel == 32 || inputFormat.mBitsPerChannel == 64)))
|
||||
if((!isFloat && !(inputFormat.mBitsPerChannel >= 1 && inputFormat.mBitsPerChannel <= 32)) || (isFloat && !(inputFormat.mBitsPerChannel == 32 || inputFormat.mBitsPerChannel == 64))) {
|
||||
inConverter = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
// These are really placeholders, as we're doing everything internally now
|
||||
if(inputLossless &&
|
||||
|
@ -530,6 +698,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
inputFormat.mChannelsPerFrame == 2 &&
|
||||
inputFormat.mSampleRate == 44100) {
|
||||
// possibly HDCD, run through decoder
|
||||
[self addObservers];
|
||||
if(hdcd_decoder) {
|
||||
free(hdcd_decoder);
|
||||
hdcd_decoder = NULL;
|
||||
|
@ -570,6 +739,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
NSUInteger samplesRead = [inChunk frameCount];
|
||||
|
||||
if(!samplesRead) {
|
||||
inConverter = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
|
@ -578,6 +748,8 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
size_t bitsPerSample = inputFormat.mBitsPerChannel;
|
||||
BOOL isBigEndian = !!(inputFormat.mFormatFlags & kAudioFormatFlagIsBigEndian);
|
||||
|
||||
double streamTimestamp = [inChunk streamTimestamp];
|
||||
|
||||
NSData *inputData = [inChunk removeSamples:samplesRead];
|
||||
|
||||
#if DSD_DECIMATE
|
||||
|
@ -637,9 +809,17 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
isFloat = YES;
|
||||
inputBuffer = &tempData[buffer_adder];
|
||||
inputChanged = YES;
|
||||
[self addObservers];
|
||||
#if DSD_DECIMATE
|
||||
float scaleFactor = 2.0f;
|
||||
vDSP_vsdiv((float *)inputBuffer, 1, &scaleFactor, (float *)inputBuffer, 1, bytesReadFromInput / sizeof(float));
|
||||
if(halveDSDVolume) {
|
||||
float scaleFactor = 2.0f;
|
||||
vDSP_vsdiv((float *)inputBuffer, 1, &scaleFactor, (float *)inputBuffer, 1, bytesReadFromInput / sizeof(float));
|
||||
}
|
||||
#else
|
||||
if(!halveDSDVolume) {
|
||||
float scaleFactor = 2.0f;
|
||||
vDSP_vsmul((float *)inputBuffer, 1, &scaleFactor, (float *)inputBuffer, 1, bytesReadFromInput / sizeof(float));
|
||||
}
|
||||
#endif
|
||||
} else if(bitsPerSample <= 8) {
|
||||
samplesRead = bytesReadFromInput;
|
||||
|
@ -657,28 +837,42 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
if(hdcd_decoder) { // implied bits per sample is 16, produces 32 bit int scale
|
||||
samplesRead = bytesReadFromInput / 2;
|
||||
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||
if(isUnsigned)
|
||||
if(isUnsigned) {
|
||||
if(!inputChanged) {
|
||||
memcpy(&tempData[buffer_adder], inputBuffer, samplesRead * 2);
|
||||
inputBuffer = &tempData[buffer_adder];
|
||||
inputChanged = YES;
|
||||
}
|
||||
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
|
||||
convert_s16_to_hdcd_input((int32_t *)(&tempData[buffer_adder]), (int16_t *)inputBuffer, samplesRead);
|
||||
hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, (int32_t *)(&tempData[buffer_adder]), (int)(samplesRead / 2));
|
||||
isUnsigned = NO;
|
||||
}
|
||||
const size_t buffer_adder2 = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||
convert_s16_to_hdcd_input((int32_t *)(&tempData[buffer_adder2]), (int16_t *)inputBuffer, samplesRead);
|
||||
hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, (int32_t *)(&tempData[buffer_adder2]), (int)(samplesRead / 2));
|
||||
if(((hdcd_state_stereo_t *)hdcd_decoder)->channel[0].sustain &&
|
||||
((hdcd_state_stereo_t *)hdcd_decoder)->channel[1].sustain) {
|
||||
hdcdSustained = YES;
|
||||
}
|
||||
gain = 2.0;
|
||||
bitsPerSample = 32;
|
||||
bytesReadFromInput = samplesRead * 4;
|
||||
isUnsigned = NO;
|
||||
inputBuffer = &tempData[buffer_adder];
|
||||
inputChanged = YES;
|
||||
if(enableHDCD) {
|
||||
gain = 2.0;
|
||||
bitsPerSample = 32;
|
||||
bytesReadFromInput = samplesRead * 4;
|
||||
isUnsigned = NO;
|
||||
inputBuffer = &tempData[buffer_adder2];
|
||||
inputChanged = YES;
|
||||
} else {
|
||||
// Discard the output of the decoder and process again
|
||||
goto process16bit;
|
||||
}
|
||||
} else if(bitsPerSample <= 16) {
|
||||
process16bit:
|
||||
samplesRead = bytesReadFromInput / 2;
|
||||
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
|
||||
if(isUnsigned) {
|
||||
if(!inputChanged) {
|
||||
memcpy(&tempData[buffer_adder], inputBuffer, samplesRead * 2);
|
||||
inputBuffer = &tempData[buffer_adder];
|
||||
inputChanged = YES;
|
||||
//inputChanged = YES;
|
||||
}
|
||||
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
|
||||
}
|
||||
|
@ -718,10 +912,10 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
vDSP_vflt32((const int *)inputBuffer, 1, (float *)(&tempData[buffer_adder]), 1, samplesRead);
|
||||
float scale = (1ULL << 31) / gain;
|
||||
vDSP_vsdiv((const float *)(&tempData[buffer_adder]), 1, &scale, (float *)(&tempData[buffer_adder]), 1, samplesRead);
|
||||
bitsPerSample = 32;
|
||||
//bitsPerSample = 32;
|
||||
bytesReadFromInput = samplesRead * sizeof(float);
|
||||
isUnsigned = NO;
|
||||
isFloat = YES;
|
||||
//isUnsigned = NO;
|
||||
//isFloat = YES;
|
||||
inputBuffer = &tempData[buffer_adder];
|
||||
}
|
||||
|
||||
|
@ -736,23 +930,47 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
|
|||
[outChunk setFormat:floatFormat];
|
||||
[outChunk setChannelConfig:inputChannelConfig];
|
||||
[outChunk setLossless:inputLossless];
|
||||
[outChunk setStreamTimestamp:streamTimestamp];
|
||||
[outChunk setStreamTimeRatio:[inChunk streamTimeRatio]];
|
||||
if(hdcdSustained) [outChunk setHDCD];
|
||||
|
||||
[outChunk assignSamples:inputBuffer frameCount:bytesReadFromInput / floatFormat.mBytesPerPacket];
|
||||
|
||||
|
||||
inConverter = NO;
|
||||
return outChunk;
|
||||
}
|
||||
|
||||
- (BOOL)peekFormat:(AudioStreamBasicDescription *)format channelConfig:(uint32_t *)config {
|
||||
if(stopping) return NO;
|
||||
inPeeker = YES;
|
||||
@synchronized(chunkList) {
|
||||
if([chunkList count]) {
|
||||
AudioChunk *chunk = [chunkList objectAtIndex:0];
|
||||
*format = [chunk format];
|
||||
*config = [chunk channelConfig];
|
||||
inPeeker = NO;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
inPeeker = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)peekTimestamp:(double *)timestamp timeRatio:(double *)timeRatio {
|
||||
if(stopping) return NO;
|
||||
inPeeker = YES;
|
||||
@synchronized (chunkList) {
|
||||
if([chunkList count]) {
|
||||
AudioChunk *chunk = [chunkList objectAtIndex:0];
|
||||
*timestamp = [chunk streamTimestamp];
|
||||
*timeRatio = [chunk streamTimeRatio];
|
||||
inPeeker = NO;
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
*timestamp = 0.0;
|
||||
*timeRatio = 1.0;
|
||||
inPeeker = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,25 +12,54 @@
|
|||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import "Node.h"
|
||||
#import <CogAudio/soxr.h>
|
||||
|
||||
#import <CogAudio/Node.h>
|
||||
|
||||
@interface ConverterNode : Node {
|
||||
NSDictionary *rgInfo;
|
||||
|
||||
soxr_t soxr;
|
||||
|
||||
void *inputBuffer;
|
||||
size_t inputBufferSize;
|
||||
size_t inpSize, inpOffset;
|
||||
|
||||
double streamTimestamp, streamTimeRatio;
|
||||
|
||||
BOOL stopping;
|
||||
BOOL convertEntered;
|
||||
BOOL paused;
|
||||
|
||||
BOOL skipResampler;
|
||||
|
||||
unsigned int PRIME_LEN_;
|
||||
unsigned int N_samples_to_add_;
|
||||
unsigned int N_samples_to_drop_;
|
||||
|
||||
BOOL is_preextrapolated_;
|
||||
int is_postextrapolated_;
|
||||
|
||||
int latencyEaten;
|
||||
int latencyEatenPost;
|
||||
|
||||
double sampleRatio;
|
||||
|
||||
BOOL observersAdded;
|
||||
|
||||
float volumeScale;
|
||||
|
||||
void *floatBuffer;
|
||||
size_t floatBufferSize;
|
||||
|
||||
void *extrapolateBuffer;
|
||||
size_t extrapolateBufferSize;
|
||||
|
||||
BOOL rememberedLossless;
|
||||
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
AudioStreamBasicDescription floatFormat;
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
|
||||
uint32_t inputChannelConfig;
|
||||
|
||||
|
@ -43,14 +72,18 @@
|
|||
|
||||
- (id)initWithController:(id)c previous:(id)p;
|
||||
|
||||
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat withInputConfig:(uint32_t)inputConfig isLossless:(BOOL)lossless;
|
||||
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat withInputConfig:(uint32_t)inputConfig outputFormat:(AudioStreamBasicDescription)outputFormat isLossless:(BOOL)lossless;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)process;
|
||||
- (AudioChunk *)convert;
|
||||
|
||||
- (void)setRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
- (void)setOutputFormat:(AudioStreamBasicDescription)outputFormat;
|
||||
|
||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig;
|
||||
|
||||
- (void)refreshVolumeScaling;
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
#import "Logging.h"
|
||||
|
||||
#import "lpc.h"
|
||||
#import "util.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#import "BadSampleCleaner.h"
|
||||
#endif
|
||||
|
@ -48,64 +51,96 @@ static void *kConverterNodeContext = &kConverterNodeContext;
|
|||
if(self) {
|
||||
rgInfo = nil;
|
||||
|
||||
soxr = 0;
|
||||
inputBuffer = NULL;
|
||||
inputBufferSize = 0;
|
||||
floatBuffer = NULL;
|
||||
floatBufferSize = 0;
|
||||
|
||||
stopping = NO;
|
||||
convertEntered = NO;
|
||||
paused = NO;
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:kConverterNodeContext];
|
||||
skipResampler = YES;
|
||||
|
||||
extrapolateBuffer = NULL;
|
||||
extrapolateBufferSize = 0;
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
[self initLogFiles];
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addObservers {
|
||||
if(!observersAdded) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:(NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew) context:kConverterNodeContext];
|
||||
observersAdded = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObservers {
|
||||
if(observersAdded) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling" context:kConverterNodeContext];
|
||||
observersAdded = NO;
|
||||
}
|
||||
}
|
||||
|
||||
void scale_by_volume(float *buffer, size_t count, float volume) {
|
||||
if(volume != 1.0) {
|
||||
size_t unaligned = (uintptr_t)buffer & 15;
|
||||
if(unaligned) {
|
||||
size_t count3 = unaligned >> 2;
|
||||
while(count3 > 0) {
|
||||
size_t count_unaligned = (16 - unaligned) / sizeof(float);
|
||||
while(count > 0 && count_unaligned > 0) {
|
||||
*buffer++ *= volume;
|
||||
count3--;
|
||||
count_unaligned--;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
vDSP_vsmul(buffer, 1, &volume, buffer, 1, count);
|
||||
if(count) {
|
||||
vDSP_vsmul(buffer, 1, &volume, buffer, 1, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
// Removed endOfStream check from here, since we want to be able to flush the converter
|
||||
// when the end of stream is reached. Convert function instead processes what it can,
|
||||
// and returns 0 samples when it has nothing more to process at the end of stream.
|
||||
while([self shouldContinue] == YES) {
|
||||
AudioChunk *chunk = nil;
|
||||
while(paused) {
|
||||
usleep(500);
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self convert];
|
||||
}
|
||||
if(!chunk) {
|
||||
if(paused) {
|
||||
continue;
|
||||
} else if(!streamFormatChanged) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@autoreleasepool {
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
endOfStream = YES;
|
||||
break;
|
||||
}
|
||||
if(paused || !streamFormatChanged) {
|
||||
continue;
|
||||
}
|
||||
usleep(500);
|
||||
} else {
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
}
|
||||
if(streamFormatChanged) {
|
||||
[self cleanUp];
|
||||
[self setupWithInputFormat:newInputFormat withInputConfig:newInputChannelConfig isLossless:rememberedLossless];
|
||||
if(streamFormatChanged) {
|
||||
[self cleanUp];
|
||||
[self setupWithInputFormat:newInputFormat withInputConfig:newInputChannelConfig outputFormat:self->outputFormat isLossless:rememberedLossless];
|
||||
}
|
||||
}
|
||||
}
|
||||
endOfStream = YES;
|
||||
}
|
||||
|
||||
- (AudioChunk *)convert {
|
||||
|
@ -121,6 +156,15 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
|
|||
return nil;
|
||||
}
|
||||
|
||||
if(inpOffset == inpSize) {
|
||||
streamTimestamp = 0.0;
|
||||
streamTimeRatio = 1.0;
|
||||
if(![self peekTimestamp:&streamTimestamp timeRatio:&streamTimeRatio]) {
|
||||
convertEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
while(inpOffset == inpSize) {
|
||||
// Approximately the most we want on input
|
||||
ioNumberPackets = 4096;
|
||||
|
@ -133,7 +177,7 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
|
|||
|
||||
ssize_t bytesReadFromInput = 0;
|
||||
|
||||
while(bytesReadFromInput < amountToWrite && !stopping && !paused && !streamFormatChanged && [self shouldContinue] == YES && [self endOfStream] == NO) {
|
||||
while(bytesReadFromInput < amountToWrite && !stopping && !paused && !streamFormatChanged && [self shouldContinue] == YES && !([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES)) {
|
||||
AudioStreamBasicDescription inf;
|
||||
uint32_t config;
|
||||
if([self peekFormat:&inf channelConfig:&config]) {
|
||||
|
@ -173,6 +217,57 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
|
|||
return nil;
|
||||
}
|
||||
|
||||
if(stopping || paused || streamFormatChanged || [self shouldContinue] == NO || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES)) {
|
||||
if(!skipResampler) {
|
||||
if(!is_postextrapolated_) {
|
||||
is_postextrapolated_ = 1;
|
||||
}
|
||||
} else {
|
||||
is_postextrapolated_ = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Extrapolate start
|
||||
if(!skipResampler && !is_preextrapolated_) {
|
||||
size_t inputSamples = bytesReadFromInput / floatFormat.mBytesPerPacket;
|
||||
size_t prime = MIN(inputSamples, PRIME_LEN_);
|
||||
size_t _N_samples_to_add_ = N_samples_to_add_;
|
||||
size_t newSize = _N_samples_to_add_ * floatFormat.mBytesPerPacket;
|
||||
newSize += bytesReadFromInput;
|
||||
|
||||
if(newSize > inputBufferSize) {
|
||||
inputBuffer = realloc(inputBuffer, inputBufferSize = newSize * 3);
|
||||
}
|
||||
|
||||
memmove(inputBuffer + _N_samples_to_add_ * floatFormat.mBytesPerPacket, inputBuffer, bytesReadFromInput);
|
||||
|
||||
lpc_extrapolate_bkwd(inputBuffer + _N_samples_to_add_ * floatFormat.mBytesPerPacket, inputSamples, prime, floatFormat.mChannelsPerFrame, LPC_ORDER, _N_samples_to_add_, &extrapolateBuffer, &extrapolateBufferSize);
|
||||
|
||||
bytesReadFromInput += _N_samples_to_add_ * floatFormat.mBytesPerPacket;
|
||||
latencyEaten = N_samples_to_drop_;
|
||||
is_preextrapolated_ = YES;
|
||||
}
|
||||
|
||||
if(is_postextrapolated_ == 1) {
|
||||
size_t inputSamples = bytesReadFromInput / floatFormat.mBytesPerPacket;
|
||||
size_t prime = MIN(inputSamples, PRIME_LEN_);
|
||||
size_t _N_samples_to_add_ = N_samples_to_add_;
|
||||
|
||||
size_t newSize = bytesReadFromInput;
|
||||
newSize += _N_samples_to_add_ * floatFormat.mBytesPerPacket;
|
||||
if(newSize > inputBufferSize) {
|
||||
inputBuffer = realloc(inputBuffer, inputBufferSize = newSize * 3);
|
||||
}
|
||||
|
||||
lpc_extrapolate_fwd(inputBuffer, inputSamples, prime, floatFormat.mChannelsPerFrame, LPC_ORDER, _N_samples_to_add_, &extrapolateBuffer, &extrapolateBufferSize);
|
||||
|
||||
bytesReadFromInput += _N_samples_to_add_ * floatFormat.mBytesPerPacket;
|
||||
latencyEatenPost = N_samples_to_drop_;
|
||||
is_postextrapolated_ = 2;
|
||||
} else if(is_postextrapolated_ == 3) {
|
||||
latencyEatenPost = 0;
|
||||
}
|
||||
|
||||
// Input now contains bytesReadFromInput worth of floats, in the input sample rate
|
||||
inpSize = bytesReadFromInput;
|
||||
inpOffset = 0;
|
||||
|
@ -182,16 +277,81 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
|
|||
|
||||
ioNumberPackets -= ioNumberPackets % floatFormat.mBytesPerPacket;
|
||||
|
||||
if(ioNumberPackets) {
|
||||
size_t inputSamples = ioNumberPackets / floatFormat.mBytesPerPacket;
|
||||
ioNumberPackets = (UInt32)inputSamples;
|
||||
ioNumberPackets = (UInt32)ceil((float)ioNumberPackets * sampleRatio);
|
||||
ioNumberPackets += soxr_delay(soxr);
|
||||
ioNumberPackets = (ioNumberPackets + 255) & ~255;
|
||||
|
||||
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
|
||||
if(!floatBuffer || floatBufferSize < newSize) {
|
||||
floatBuffer = realloc(floatBuffer, floatBufferSize = newSize * 3);
|
||||
}
|
||||
|
||||
if(stopping) {
|
||||
convertEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
size_t inputDone = 0;
|
||||
size_t outputDone = 0;
|
||||
|
||||
if(!skipResampler) {
|
||||
soxr_process(soxr, (float *)(((uint8_t *)inputBuffer) + inpOffset), inputSamples, &inputDone, floatBuffer, ioNumberPackets, &outputDone);
|
||||
|
||||
if(latencyEatenPost) {
|
||||
// Post file or format change flush
|
||||
size_t idone = 0, odone = 0;
|
||||
|
||||
do {
|
||||
soxr_process(soxr, NULL, 0, &idone, floatBuffer + outputDone * floatFormat.mBytesPerPacket, ioNumberPackets - outputDone, &odone);
|
||||
outputDone += odone;
|
||||
} while(odone > 0);
|
||||
}
|
||||
} else {
|
||||
memcpy(floatBuffer, (((uint8_t *)inputBuffer) + inpOffset), inputSamples * floatFormat.mBytesPerPacket);
|
||||
inputDone = inputSamples;
|
||||
outputDone = inputSamples;
|
||||
}
|
||||
|
||||
inpOffset += inputDone * floatFormat.mBytesPerPacket;
|
||||
|
||||
if(latencyEaten) {
|
||||
if(outputDone > latencyEaten) {
|
||||
outputDone -= latencyEaten;
|
||||
memmove(floatBuffer, floatBuffer + latencyEaten * floatFormat.mBytesPerPacket, outputDone * floatFormat.mBytesPerPacket);
|
||||
latencyEaten = 0;
|
||||
} else {
|
||||
latencyEaten -= outputDone;
|
||||
outputDone = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(latencyEatenPost) {
|
||||
if(outputDone > latencyEatenPost) {
|
||||
outputDone -= latencyEatenPost;
|
||||
} else {
|
||||
outputDone = 0;
|
||||
}
|
||||
latencyEatenPost = 0;
|
||||
}
|
||||
|
||||
ioNumberPackets = (UInt32)outputDone * floatFormat.mBytesPerPacket;
|
||||
}
|
||||
|
||||
if(ioNumberPackets) {
|
||||
AudioChunk *chunk = [[AudioChunk alloc] init];
|
||||
[chunk setFormat:nodeFormat];
|
||||
if(nodeChannelConfig) {
|
||||
[chunk setChannelConfig:nodeChannelConfig];
|
||||
}
|
||||
scale_by_volume((float *)(((uint8_t *)inputBuffer) + inpOffset), ioNumberPackets / sizeof(float), volumeScale);
|
||||
[chunk assignSamples:(((uint8_t *)inputBuffer) + inpOffset) frameCount:ioNumberPackets / floatFormat.mBytesPerPacket];
|
||||
|
||||
inpOffset += ioNumberPackets;
|
||||
[self addObservers];
|
||||
scale_by_volume(floatBuffer, ioNumberPackets / sizeof(float), volumeScale);
|
||||
[chunk setStreamTimestamp:streamTimestamp];
|
||||
[chunk setStreamTimeRatio:streamTimeRatio];
|
||||
[chunk assignSamples:floatBuffer frameCount:ioNumberPackets / floatFormat.mBytesPerPacket];
|
||||
streamTimestamp += [chunk durationRatioed];
|
||||
convertEntered = NO;
|
||||
return chunk;
|
||||
}
|
||||
|
@ -260,9 +420,10 @@ static float db_to_scale(float db) {
|
|||
volumeScale = scale;
|
||||
}
|
||||
|
||||
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf withInputConfig:(uint32_t)inputConfig isLossless:(BOOL)lossless {
|
||||
- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf withInputConfig:(uint32_t)inputConfig outputFormat:(AudioStreamBasicDescription)outf isLossless:(BOOL)lossless {
|
||||
// Make the converter
|
||||
inputFormat = inf;
|
||||
outputFormat = outf;
|
||||
|
||||
inputChannelConfig = inputConfig;
|
||||
|
||||
|
@ -289,11 +450,40 @@ static float db_to_scale(float db) {
|
|||
inpOffset = 0;
|
||||
inpSize = 0;
|
||||
|
||||
// This is a post resampler, post-down/upmix format
|
||||
// This is a post resampler format
|
||||
|
||||
nodeFormat = floatFormat;
|
||||
nodeFormat.mSampleRate = outputFormat.mSampleRate;
|
||||
nodeChannelConfig = inputChannelConfig;
|
||||
|
||||
sampleRatio = (double)outputFormat.mSampleRate / (double)floatFormat.mSampleRate;
|
||||
skipResampler = fabs(sampleRatio - 1.0) < 1e-7;
|
||||
if(!skipResampler) {
|
||||
soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, 0);
|
||||
soxr_io_spec_t io_spec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
|
||||
soxr_runtime_spec_t runtime_spec = soxr_runtime_spec(0);
|
||||
soxr_error_t error;
|
||||
|
||||
soxr = soxr_create(floatFormat.mSampleRate, outputFormat.mSampleRate, floatFormat.mChannelsPerFrame, &error, &io_spec, &q_spec, &runtime_spec);
|
||||
if(error)
|
||||
return NO;
|
||||
|
||||
PRIME_LEN_ = MAX(floatFormat.mSampleRate / 20, 1024u);
|
||||
PRIME_LEN_ = MIN(PRIME_LEN_, 16384u);
|
||||
PRIME_LEN_ = MAX(PRIME_LEN_, (unsigned int)(2 * LPC_ORDER + 1));
|
||||
|
||||
N_samples_to_add_ = floatFormat.mSampleRate;
|
||||
N_samples_to_drop_ = outputFormat.mSampleRate;
|
||||
|
||||
samples_len(&N_samples_to_add_, &N_samples_to_drop_, 20, 8192u);
|
||||
|
||||
is_preextrapolated_ = NO;
|
||||
is_postextrapolated_ = 0;
|
||||
}
|
||||
|
||||
latencyEaten = 0;
|
||||
latencyEatenPost = 0;
|
||||
|
||||
PrintStreamDesc(&inf);
|
||||
PrintStreamDesc(&nodeFormat);
|
||||
|
||||
|
@ -309,12 +499,18 @@ static float db_to_scale(float db) {
|
|||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"Decoder dealloc");
|
||||
DLog(@"Converter dealloc");
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling" context:kConverterNodeContext];
|
||||
[self removeObservers];
|
||||
|
||||
paused = NO;
|
||||
[self cleanUp];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
- (void)setOutputFormat:(AudioStreamBasicDescription)format {
|
||||
DLog(@"SETTING OUTPUT FORMAT!");
|
||||
outputFormat = format;
|
||||
}
|
||||
|
||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig {
|
||||
|
@ -324,7 +520,7 @@ static float db_to_scale(float db) {
|
|||
usleep(500);
|
||||
}
|
||||
[self cleanUp];
|
||||
[self setupWithInputFormat:format withInputConfig:inputConfig isLossless:rememberedLossless];
|
||||
[self setupWithInputFormat:format withInputConfig:inputConfig outputFormat:self->outputFormat isLossless:rememberedLossless];
|
||||
}
|
||||
|
||||
- (void)setRGInfo:(NSDictionary *)rgi {
|
||||
|
@ -338,6 +534,20 @@ static float db_to_scale(float db) {
|
|||
while(convertEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
if(soxr) {
|
||||
soxr_delete(soxr);
|
||||
soxr = NULL;
|
||||
}
|
||||
if(extrapolateBuffer) {
|
||||
free(extrapolateBuffer);
|
||||
extrapolateBuffer = NULL;
|
||||
extrapolateBufferSize = 0;
|
||||
}
|
||||
if(floatBuffer) {
|
||||
free(floatBuffer);
|
||||
floatBuffer = NULL;
|
||||
floatBufferSize = 0;
|
||||
}
|
||||
if(inputBuffer) {
|
||||
free(inputBuffer);
|
||||
inputBuffer = NULL;
|
||||
|
|
34
Audio/Chain/DSP/DSPDownmixNode.h
Normal file
34
Audio/Chain/DSP/DSPDownmixNode.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// DSPDownmixNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/13/25.
|
||||
//
|
||||
|
||||
#ifndef DSPDownmixNode_h
|
||||
#define DSPDownmixNode_h
|
||||
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#import <CogAudio/DSPNode.h>
|
||||
|
||||
@interface DSPDownmixNode : DSPNode {
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (void)resetBuffer;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)process;
|
||||
- (AudioChunk * _Nullable)convert;
|
||||
|
||||
- (void)setOutputFormat:(AudioStreamBasicDescription)format withChannelConfig:(uint32_t)config;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* DSPDownmixNode_h */
|
201
Audio/Chain/DSP/DSPDownmixNode.m
Normal file
201
Audio/Chain/DSP/DSPDownmixNode.m
Normal file
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// DSPDownmixNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/13/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "Downmix.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#import "DSPDownmixNode.h"
|
||||
|
||||
@implementation DSPDownmixNode {
|
||||
DownmixProcessor *downmix;
|
||||
|
||||
BOOL stopping, paused;
|
||||
BOOL processEntered;
|
||||
BOOL formatSet;
|
||||
|
||||
AudioStreamBasicDescription lastInputFormat;
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
|
||||
uint32_t lastInputChannelConfig, inputChannelConfig;
|
||||
uint32_t outputChannelConfig;
|
||||
|
||||
float outBuffer[4096 * 32];
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super initWithController:c previous:p latency:latency];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"Downmix dealloc");
|
||||
[self setShouldContinue:NO];
|
||||
[self cleanUp];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
- (BOOL)fullInit {
|
||||
if(formatSet) {
|
||||
downmix = [[DownmixProcessor alloc] initWithInputFormat:inputFormat inputConfig:inputChannelConfig andOutputFormat:outputFormat outputConfig:outputChannelConfig];
|
||||
if(!downmix) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)fullShutdown {
|
||||
downmix = nil;
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
if(stopping)
|
||||
return NO;
|
||||
[self fullShutdown];
|
||||
return [self fullInit];
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
stopping = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[self fullShutdown];
|
||||
formatSet = NO;
|
||||
}
|
||||
|
||||
- (void)resetBuffer {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
paused = NO;
|
||||
}
|
||||
|
||||
- (void)setOutputFormat:(AudioStreamBasicDescription)format withChannelConfig:(uint32_t)config {
|
||||
if(memcmp(&outputFormat, &format, sizeof(outputFormat)) != 0 ||
|
||||
outputChannelConfig != config) {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
[self fullShutdown];
|
||||
paused = NO;
|
||||
}
|
||||
outputFormat = format;
|
||||
outputChannelConfig = config;
|
||||
formatSet = YES;
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
while([self shouldContinue] == YES) {
|
||||
if(paused || endOfStream) {
|
||||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self convert];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if([previousNode endOfStream] == YES) {
|
||||
usleep(500);
|
||||
endOfStream = YES;
|
||||
continue;
|
||||
}
|
||||
if(paused) {
|
||||
continue;
|
||||
}
|
||||
usleep(500);
|
||||
} else {
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)convert {
|
||||
if(stopping)
|
||||
return nil;
|
||||
|
||||
processEntered = YES;
|
||||
|
||||
if(stopping || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) || [self shouldContinue] == NO) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(![self peekFormat:&inputFormat channelConfig:&inputChannelConfig]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!inputFormat.mSampleRate ||
|
||||
!inputFormat.mBitsPerChannel ||
|
||||
!inputFormat.mChannelsPerFrame ||
|
||||
!inputFormat.mBytesPerFrame ||
|
||||
!inputFormat.mFramesPerPacket ||
|
||||
!inputFormat.mBytesPerPacket) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((formatSet && !downmix) ||
|
||||
memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 ||
|
||||
inputChannelConfig != lastInputChannelConfig) {
|
||||
lastInputFormat = inputFormat;
|
||||
lastInputChannelConfig = inputChannelConfig;
|
||||
[self fullShutdown];
|
||||
if(formatSet && ![self setup]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(!downmix) {
|
||||
processEntered = NO;
|
||||
return [self readChunk:4096];
|
||||
}
|
||||
|
||||
AudioChunk *chunk = [self readChunkAsFloat32:4096];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
double streamTimestamp = [chunk streamTimestamp];
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
[downmix process:[sampleData bytes] frameCount:frameCount output:&outBuffer[0]];
|
||||
|
||||
AudioChunk *outputChunk = [[AudioChunk alloc] init];
|
||||
[outputChunk setFormat:outputFormat];
|
||||
if(outputChannelConfig) {
|
||||
[outputChunk setChannelConfig:outputChannelConfig];
|
||||
}
|
||||
if([chunk isHDCD]) [outputChunk setHDCD];
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:[chunk streamTimeRatio]];
|
||||
[outputChunk assignSamples:&outBuffer[0] frameCount:frameCount];
|
||||
|
||||
processEntered = NO;
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
@end
|
31
Audio/Chain/DSP/DSPEqualizerNode.h
Normal file
31
Audio/Chain/DSP/DSPEqualizerNode.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// DSPEqualizerNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/11/25.
|
||||
//
|
||||
|
||||
#ifndef DSPEqualizerNode_h
|
||||
#define DSPEqualizerNode_h
|
||||
|
||||
#import <CogAudio/DSPNode.h>
|
||||
|
||||
@interface DSPEqualizerNode : DSPNode {
|
||||
float *samplePtr;
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (void)resetBuffer;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)process;
|
||||
- (AudioChunk * _Nullable)convert;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* DSPEqualizerNode_h */
|
400
Audio/Chain/DSP/DSPEqualizerNode.m
Normal file
400
Audio/Chain/DSP/DSPEqualizerNode.m
Normal file
|
@ -0,0 +1,400 @@
|
|||
//
|
||||
// DSPEqualizerNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/11/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
#import "DSPEqualizerNode.h"
|
||||
|
||||
#import "OutputNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#import "AudioPlayer.h"
|
||||
|
||||
extern void scale_by_volume(float *buffer, size_t count, float volume);
|
||||
|
||||
static void * kDSPEqualizerNodeContext = &kDSPEqualizerNodeContext;
|
||||
|
||||
@implementation DSPEqualizerNode {
|
||||
BOOL enableEqualizer;
|
||||
BOOL equalizerInitialized;
|
||||
|
||||
double equalizerPreamp;
|
||||
|
||||
__weak AudioPlayer *audioPlayer;
|
||||
|
||||
AudioUnit _eq;
|
||||
|
||||
AudioTimeStamp timeStamp;
|
||||
|
||||
BOOL stopping, paused;
|
||||
BOOL processEntered;
|
||||
|
||||
BOOL observersapplied;
|
||||
|
||||
AudioStreamBasicDescription lastInputFormat;
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
|
||||
uint32_t lastInputChannelConfig, inputChannelConfig;
|
||||
uint32_t outputChannelConfig;
|
||||
|
||||
float inBuffer[4096 * 32];
|
||||
float eqBuffer[4096 * 32];
|
||||
float outBuffer[4096 * 32];
|
||||
}
|
||||
|
||||
static void fillBuffers(AudioBufferList *ioData, const float *inbuffer, size_t count, size_t offset) {
|
||||
const size_t channels = ioData->mNumberBuffers;
|
||||
for(int i = 0; i < channels; ++i) {
|
||||
const size_t maxCount = (ioData->mBuffers[i].mDataByteSize / sizeof(float)) - offset;
|
||||
float *output = ((float *)ioData->mBuffers[i].mData) + offset;
|
||||
const float *input = inbuffer + i;
|
||||
cblas_scopy((int)((count > maxCount) ? maxCount : count), input, (int)channels, output, 1);
|
||||
ioData->mBuffers[i].mNumberChannels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void clearBuffers(AudioBufferList *ioData, size_t count, size_t offset) {
|
||||
for(int i = 0; i < ioData->mNumberBuffers; ++i) {
|
||||
memset((uint8_t *)ioData->mBuffers[i].mData + offset * sizeof(float), 0, count * sizeof(float));
|
||||
ioData->mBuffers[i].mNumberChannels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
|
||||
if(inNumberFrames > 4096 || !inRefCon) {
|
||||
clearBuffers(ioData, inNumberFrames, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSPEqualizerNode *_self = (__bridge DSPEqualizerNode *)inRefCon;
|
||||
|
||||
fillBuffers(ioData, _self->samplePtr, inNumberFrames, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super initWithController:c previous:p latency:latency];
|
||||
if(self) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableEqualizer = [defaults boolForKey:@"GraphicEQenable"];
|
||||
|
||||
float preamp = [defaults floatForKey:@"eqPreamp"];
|
||||
equalizerPreamp = pow(10.0, preamp / 20.0);
|
||||
|
||||
OutputNode *outputNode = c;
|
||||
audioPlayer = [outputNode controller];
|
||||
|
||||
[self addObservers];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"Equalizer dealloc");
|
||||
[self setShouldContinue:NO];
|
||||
[self cleanUp];
|
||||
[self removeObservers];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
- (void)addObservers {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:kDSPEqualizerNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.eqPreamp" options:0 context:kDSPEqualizerNodeContext];
|
||||
|
||||
observersapplied = YES;
|
||||
}
|
||||
|
||||
- (void)removeObservers {
|
||||
if(observersapplied) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable" context:kDSPEqualizerNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.eqPreamp" context:kDSPEqualizerNodeContext];
|
||||
observersapplied = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if(context != kDSPEqualizerNodeContext) {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
return;
|
||||
}
|
||||
|
||||
if([keyPath isEqualToString:@"values.GraphicEQenable"]) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableEqualizer = [defaults boolForKey:@"GraphicEQenable"];
|
||||
} else if([keyPath isEqualToString:@"values.eqPreamp"]) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
float preamp = [defaults floatForKey:@"eqPreamp"];
|
||||
equalizerPreamp = pow(10.0, preamp / 20.0);
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioPlayer *)audioPlayer {
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
- (BOOL)fullInit {
|
||||
if(enableEqualizer) {
|
||||
AudioComponentDescription desc;
|
||||
|
||||
desc.componentType = kAudioUnitType_Effect;
|
||||
desc.componentSubType = kAudioUnitSubType_GraphicEQ;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
AudioComponent comp = NULL;
|
||||
|
||||
comp = AudioComponentFindNext(comp, &desc);
|
||||
if(!comp) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
OSStatus status = AudioComponentInstanceNew(comp, &_eq);
|
||||
if(status != noErr) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
UInt32 value;
|
||||
UInt32 size = sizeof(value);
|
||||
|
||||
value = 4096;
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_MaximumFramesPerSlice,
|
||||
kAudioUnitScope_Global, 0, &value, size);
|
||||
|
||||
value = 127;
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_RenderQuality,
|
||||
kAudioUnitScope_Global, 0, &value, size);
|
||||
|
||||
AURenderCallbackStruct callbackStruct;
|
||||
callbackStruct.inputProcRefCon = (__bridge void *)self;
|
||||
callbackStruct.inputProc = eqRenderCallback;
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &callbackStruct, sizeof(callbackStruct));
|
||||
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Output, 0);
|
||||
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Global, 0);
|
||||
|
||||
AudioStreamBasicDescription asbd = inputFormat;
|
||||
|
||||
// Of course, non-interleaved has only one sample per frame/packet, per buffer
|
||||
asbd.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
|
||||
asbd.mBytesPerFrame = sizeof(float);
|
||||
asbd.mBytesPerPacket = sizeof(float);
|
||||
asbd.mFramesPerPacket = 1;
|
||||
|
||||
UInt32 maximumFrames = 4096;
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maximumFrames, sizeof(maximumFrames));
|
||||
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &asbd, sizeof(asbd));
|
||||
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Output, 0, &asbd, sizeof(asbd));
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Output, 0);
|
||||
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Global, 0);
|
||||
|
||||
status = AudioUnitInitialize(_eq);
|
||||
if(status != noErr) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
bzero(&timeStamp, sizeof(timeStamp));
|
||||
timeStamp.mFlags = kAudioTimeStampSampleTimeValid;
|
||||
|
||||
equalizerInitialized = YES;
|
||||
|
||||
[[self audioPlayer] beginEqualizer:_eq];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)fullShutdown {
|
||||
if(_eq) {
|
||||
if(equalizerInitialized) {
|
||||
[[self audioPlayer] endEqualizer:_eq];
|
||||
AudioUnitUninitialize(_eq);
|
||||
equalizerInitialized = NO;
|
||||
}
|
||||
AudioComponentInstanceDispose(_eq);
|
||||
_eq = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
if(stopping)
|
||||
return NO;
|
||||
[self fullShutdown];
|
||||
return [self fullInit];
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
stopping = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[self fullShutdown];
|
||||
}
|
||||
|
||||
- (void)resetBuffer {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
[self fullShutdown];
|
||||
paused = NO;
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
while([self shouldContinue] == YES) {
|
||||
if(paused || endOfStream) {
|
||||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self convert];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if([previousNode endOfStream] == YES) {
|
||||
usleep(500);
|
||||
endOfStream = YES;
|
||||
continue;
|
||||
}
|
||||
if(paused) {
|
||||
continue;
|
||||
}
|
||||
usleep(500);
|
||||
} else {
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
if(!enableEqualizer && equalizerInitialized) {
|
||||
[self fullShutdown];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)convert {
|
||||
if(stopping)
|
||||
return nil;
|
||||
|
||||
processEntered = YES;
|
||||
|
||||
if(stopping || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) || [self shouldContinue] == NO) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(![self peekFormat:&inputFormat channelConfig:&inputChannelConfig]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!inputFormat.mSampleRate ||
|
||||
!inputFormat.mBitsPerChannel ||
|
||||
!inputFormat.mChannelsPerFrame ||
|
||||
!inputFormat.mBytesPerFrame ||
|
||||
!inputFormat.mFramesPerPacket ||
|
||||
!inputFormat.mBytesPerPacket) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((enableEqualizer && !equalizerInitialized) ||
|
||||
memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 ||
|
||||
inputChannelConfig != lastInputChannelConfig) {
|
||||
lastInputFormat = inputFormat;
|
||||
lastInputChannelConfig = inputChannelConfig;
|
||||
[self fullShutdown];
|
||||
if(enableEqualizer && ![self setup]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(!equalizerInitialized) {
|
||||
processEntered = NO;
|
||||
return [self readChunk:4096];
|
||||
}
|
||||
|
||||
AudioChunk *chunk = [self readChunkAsFloat32:4096];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
double streamTimestamp = [chunk streamTimestamp];
|
||||
|
||||
samplePtr = &inBuffer[0];
|
||||
size_t channels = inputFormat.mChannelsPerFrame;
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
cblas_scopy((int)(frameCount * channels), [sampleData bytes], 1, &inBuffer[0], 1);
|
||||
|
||||
const size_t channelsminusone = channels - 1;
|
||||
uint8_t tempBuffer[sizeof(AudioBufferList) + sizeof(AudioBuffer) * channelsminusone];
|
||||
AudioBufferList *ioData = (AudioBufferList *)&tempBuffer[0];
|
||||
|
||||
ioData->mNumberBuffers = (UInt32)channels;
|
||||
for(size_t i = 0; i < channels; ++i) {
|
||||
ioData->mBuffers[i].mData = &eqBuffer[4096 * i];
|
||||
ioData->mBuffers[i].mDataByteSize = (UInt32)(frameCount * sizeof(float));
|
||||
ioData->mBuffers[i].mNumberChannels = 1;
|
||||
}
|
||||
|
||||
OSStatus status = AudioUnitRender(_eq, NULL, &timeStamp, 0, (UInt32)frameCount, ioData);
|
||||
|
||||
if(status != noErr) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
timeStamp.mSampleTime += ((double)frameCount) / inputFormat.mSampleRate;
|
||||
|
||||
for(int i = 0; i < channels; ++i) {
|
||||
cblas_scopy((int)frameCount, &eqBuffer[4096 * i], 1, &outBuffer[i], (int)channels);
|
||||
}
|
||||
|
||||
AudioChunk *outputChunk = nil;
|
||||
if(frameCount) {
|
||||
scale_by_volume(&outBuffer[0], frameCount * channels, equalizerPreamp);
|
||||
|
||||
outputChunk = [[AudioChunk alloc] init];
|
||||
[outputChunk setFormat:inputFormat];
|
||||
if(outputChannelConfig) {
|
||||
[outputChunk setChannelConfig:inputChannelConfig];
|
||||
}
|
||||
if([chunk isHDCD]) [outputChunk setHDCD];
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:[chunk streamTimeRatio]];
|
||||
[outputChunk assignSamples:&outBuffer[0] frameCount:frameCount];
|
||||
}
|
||||
|
||||
processEntered = NO;
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
@end
|
30
Audio/Chain/DSP/DSPFSurroundNode.h
Normal file
30
Audio/Chain/DSP/DSPFSurroundNode.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// DSPFSurroundNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/11/25.
|
||||
//
|
||||
|
||||
#ifndef DSPFSurroundNode_h
|
||||
#define DSPFSurroundNode_h
|
||||
|
||||
#import <CogAudio/DSPNode.h>
|
||||
|
||||
@interface DSPFSurroundNode : DSPNode {
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (void)resetBuffer;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)process;
|
||||
- (AudioChunk * _Nullable)convert;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* DSPFSurroundNode_h */
|
275
Audio/Chain/DSP/DSPFSurroundNode.m
Normal file
275
Audio/Chain/DSP/DSPFSurroundNode.m
Normal file
|
@ -0,0 +1,275 @@
|
|||
//
|
||||
// DSPFSurroundNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/11/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
#import "DSPFSurroundNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#import "FSurroundFilter.h"
|
||||
|
||||
#define OCTAVES 5
|
||||
|
||||
static void * kDSPFSurroundNodeContext = &kDSPFSurroundNodeContext;
|
||||
|
||||
@implementation DSPFSurroundNode {
|
||||
BOOL enableFSurround;
|
||||
BOOL FSurroundDelayRemoved;
|
||||
FSurroundFilter *fsurround;
|
||||
|
||||
BOOL stopping, paused;
|
||||
BOOL processEntered;
|
||||
|
||||
BOOL observersapplied;
|
||||
|
||||
AudioStreamBasicDescription lastInputFormat;
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
|
||||
uint32_t lastInputChannelConfig, inputChannelConfig;
|
||||
uint32_t outputChannelConfig;
|
||||
|
||||
float inBuffer[4096 * 2];
|
||||
float outBuffer[8192 * 6];
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super initWithController:c previous:p latency:latency];
|
||||
if(self) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableFSurround = [defaults boolForKey:@"enableFSurround"];
|
||||
|
||||
[self addObservers];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"FreeSurround dealloc");
|
||||
[self setShouldContinue:NO];
|
||||
[self cleanUp];
|
||||
[self removeObservers];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
- (void)addObservers {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableFSurround" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPFSurroundNodeContext];
|
||||
|
||||
observersapplied = YES;
|
||||
}
|
||||
|
||||
- (void)removeObservers {
|
||||
if(observersapplied) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableFSurround" context:kDSPFSurroundNodeContext];
|
||||
observersapplied = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if(context != kDSPFSurroundNodeContext) {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
return;
|
||||
}
|
||||
|
||||
if([keyPath isEqualToString:@"values.enableFSurround"]) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableFSurround = [defaults boolForKey:@"enableFSurround"];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)fullInit {
|
||||
if(enableFSurround && inputFormat.mChannelsPerFrame == 2) {
|
||||
fsurround = [[FSurroundFilter alloc] initWithSampleRate:inputFormat.mSampleRate];
|
||||
if(!fsurround) {
|
||||
return NO;
|
||||
}
|
||||
outputFormat = inputFormat;
|
||||
outputFormat.mChannelsPerFrame = [fsurround channelCount];
|
||||
outputFormat.mBytesPerFrame = sizeof(float) * outputFormat.mChannelsPerFrame;
|
||||
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
|
||||
outputChannelConfig = [fsurround channelConfig];
|
||||
|
||||
FSurroundDelayRemoved = NO;
|
||||
} else {
|
||||
fsurround = nil;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)fullShutdown {
|
||||
fsurround = nil;
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
if(stopping)
|
||||
return NO;
|
||||
[self fullShutdown];
|
||||
return [self fullInit];
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
stopping = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[self fullShutdown];
|
||||
}
|
||||
|
||||
- (void)resetBuffer {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
[self fullShutdown];
|
||||
paused = NO;
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
while([self shouldContinue] == YES) {
|
||||
if(paused || endOfStream) {
|
||||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self convert];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if([previousNode endOfStream] == YES) {
|
||||
usleep(500);
|
||||
endOfStream = YES;
|
||||
continue;
|
||||
}
|
||||
if(paused) {
|
||||
continue;
|
||||
}
|
||||
usleep(500);
|
||||
} else {
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
if(!enableFSurround && fsurround) {
|
||||
[self fullShutdown];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)convert {
|
||||
if(stopping)
|
||||
return nil;
|
||||
|
||||
processEntered = YES;
|
||||
|
||||
if(stopping || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) || [self shouldContinue] == NO) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(![self peekFormat:&inputFormat channelConfig:&inputChannelConfig]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!inputFormat.mSampleRate ||
|
||||
!inputFormat.mBitsPerChannel ||
|
||||
!inputFormat.mChannelsPerFrame ||
|
||||
!inputFormat.mBytesPerFrame ||
|
||||
!inputFormat.mFramesPerPacket ||
|
||||
!inputFormat.mBytesPerPacket) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((enableFSurround && !fsurround) ||
|
||||
memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 ||
|
||||
inputChannelConfig != lastInputChannelConfig) {
|
||||
lastInputFormat = inputFormat;
|
||||
lastInputChannelConfig = inputChannelConfig;
|
||||
[self fullShutdown];
|
||||
if(enableFSurround && ![self setup]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(!fsurround) {
|
||||
processEntered = NO;
|
||||
return [self readChunk:4096];
|
||||
}
|
||||
|
||||
size_t totalRequestedSamples = 4096;
|
||||
|
||||
size_t totalFrameCount = 0;
|
||||
AudioChunk *chunk = [self readAndMergeChunksAsFloat32:totalRequestedSamples];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
double streamTimestamp = [chunk streamTimestamp];
|
||||
|
||||
float *samplePtr = &inBuffer[0];
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
cblas_scopy((int)frameCount * 2, [sampleData bytes], 1, &samplePtr[0], 1);
|
||||
|
||||
totalFrameCount = frameCount;
|
||||
|
||||
size_t countToProcess = totalFrameCount;
|
||||
size_t samplesRendered;
|
||||
if(countToProcess < 4096) {
|
||||
bzero(&inBuffer[countToProcess * 2], (4096 - countToProcess) * 2 * sizeof(float));
|
||||
countToProcess = 4096;
|
||||
}
|
||||
|
||||
[fsurround process:&inBuffer[0] output:&outBuffer[0] count:(int)countToProcess];
|
||||
samplePtr = &outBuffer[0];
|
||||
samplesRendered = totalFrameCount;
|
||||
|
||||
if(totalFrameCount < 4096) {
|
||||
bzero(&outBuffer[4096 * 6], 4096 * 2 * sizeof(float));
|
||||
[fsurround process:&outBuffer[4096 * 6] output:&outBuffer[4096 * 6] count:4096];
|
||||
samplesRendered += 2048;
|
||||
}
|
||||
|
||||
if(!FSurroundDelayRemoved) {
|
||||
FSurroundDelayRemoved = YES;
|
||||
if(samplesRendered > 2048) {
|
||||
samplePtr += 2048 * 6;
|
||||
samplesRendered -= 2048;
|
||||
}
|
||||
}
|
||||
|
||||
AudioChunk *outputChunk = nil;
|
||||
if(samplesRendered) {
|
||||
outputChunk = [[AudioChunk alloc] init];
|
||||
[outputChunk setFormat:outputFormat];
|
||||
if(outputChannelConfig) {
|
||||
[outputChunk setChannelConfig:outputChannelConfig];
|
||||
}
|
||||
if([chunk isHDCD]) [outputChunk setHDCD];
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:[chunk streamTimeRatio]];
|
||||
[outputChunk assignSamples:samplePtr frameCount:samplesRendered];
|
||||
}
|
||||
|
||||
processEntered = NO;
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
@end
|
35
Audio/Chain/DSP/DSPHRTFNode.h
Normal file
35
Audio/Chain/DSP/DSPHRTFNode.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// DSPHRTFNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/11/25.
|
||||
//
|
||||
|
||||
#ifndef DSPHRTFNode_h
|
||||
#define DSPHRTFNode_h
|
||||
|
||||
#import <simd/types.h>
|
||||
|
||||
#import <CogAudio/DSPNode.h>
|
||||
|
||||
@interface DSPHRTFNode : DSPNode {
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (void)resetBuffer;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)process;
|
||||
- (AudioChunk * _Nullable)convert;
|
||||
|
||||
- (void)reportMotion:(simd_float4x4)matrix;
|
||||
- (void)resetReferencePosition:(NSNotification *_Nullable)notification;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* DSPHRTFNode_h */
|
435
Audio/Chain/DSP/DSPHRTFNode.m
Normal file
435
Audio/Chain/DSP/DSPHRTFNode.m
Normal file
|
@ -0,0 +1,435 @@
|
|||
//
|
||||
// DSPHRTFNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/11/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#import "DSPHRTFNode.h"
|
||||
|
||||
#import "lpc.h"
|
||||
|
||||
#import "HeadphoneFilter.h"
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
#if defined(MAC_OS_VERSION_14_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0
|
||||
#define MOTION_MANAGER 1
|
||||
#endif
|
||||
|
||||
static void * kDSPHRTFNodeContext = &kDSPHRTFNodeContext;
|
||||
|
||||
static NSString *CogPlaybackDidResetHeadTracking = @"CogPlaybackDigResetHeadTracking";
|
||||
|
||||
#ifdef MOTION_MANAGER
|
||||
static simd_float4x4 convertMatrix(CMRotationMatrix r) {
|
||||
simd_float4x4 matrix = {
|
||||
simd_make_float4(r.m33, -r.m31, r.m32, 0.0f),
|
||||
simd_make_float4(r.m13, -r.m11, r.m12, 0.0f),
|
||||
simd_make_float4(r.m23, -r.m21, r.m22, 0.0f),
|
||||
simd_make_float4(0.0f, 0.0f, 0.0f, 1.0f)
|
||||
};
|
||||
return matrix;
|
||||
}
|
||||
|
||||
static NSLock *motionManagerLock = nil;
|
||||
API_AVAILABLE(macos(14.0)) static CMHeadphoneMotionManager *motionManager = nil;
|
||||
static DSPHRTFNode __weak *registeredMotionListener = nil;
|
||||
#endif
|
||||
|
||||
static void registerMotionListener(DSPHRTFNode *listener) {
|
||||
#ifdef MOTION_MANAGER
|
||||
if(@available(macOS 14, *)) {
|
||||
[motionManagerLock lock];
|
||||
if([motionManager isDeviceMotionActive]) {
|
||||
[motionManager stopDeviceMotionUpdates];
|
||||
}
|
||||
if([motionManager isDeviceMotionAvailable]) {
|
||||
registeredMotionListener = listener;
|
||||
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
|
||||
if(motion) {
|
||||
[motionManagerLock lock];
|
||||
if(registeredMotionListener)
|
||||
[registeredMotionListener reportMotion:convertMatrix(motion.attitude.rotationMatrix)];
|
||||
[motionManagerLock unlock];
|
||||
}
|
||||
}];
|
||||
}
|
||||
[motionManagerLock unlock];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void unregisterMotionListener(void) {
|
||||
#ifdef MOTION_MANAGER
|
||||
if(@available(macOS 14, *)) {
|
||||
[motionManagerLock lock];
|
||||
if([motionManager isDeviceMotionActive]) {
|
||||
[motionManager stopDeviceMotionUpdates];
|
||||
}
|
||||
registeredMotionListener = nil;
|
||||
[motionManagerLock unlock];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@implementation DSPHRTFNode {
|
||||
BOOL enableHrtf;
|
||||
BOOL enableHeadTracking;
|
||||
BOOL lastEnableHeadTracking;
|
||||
|
||||
HeadphoneFilter *hrtf;
|
||||
|
||||
BOOL stopping, paused;
|
||||
BOOL processEntered;
|
||||
BOOL resetFilter;
|
||||
|
||||
size_t needPrefill;
|
||||
|
||||
BOOL observersapplied;
|
||||
|
||||
AudioStreamBasicDescription lastInputFormat;
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
|
||||
uint32_t lastInputChannelConfig, inputChannelConfig;
|
||||
uint32_t outputChannelConfig;
|
||||
|
||||
BOOL referenceMatrixSet;
|
||||
BOOL rotationMatrixUpdated;
|
||||
simd_float4x4 rotationMatrix;
|
||||
simd_float4x4 referenceMatrix;
|
||||
|
||||
float prefillBuffer[4096 * 32];
|
||||
float outBuffer[4096 * 2];
|
||||
|
||||
void *extrapolate_buffer;
|
||||
size_t extrapolate_buffer_size;
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
#ifdef MOTION_MANAGER
|
||||
motionManagerLock = [[NSLock alloc] init];
|
||||
|
||||
if(@available(macOS 14, *)) {
|
||||
CMAuthorizationStatus status = [CMHeadphoneMotionManager authorizationStatus];
|
||||
if(status == CMAuthorizationStatusDenied) {
|
||||
ALog(@"Headphone motion not authorized");
|
||||
return;
|
||||
} else if(status == CMAuthorizationStatusAuthorized) {
|
||||
ALog(@"Headphone motion authorized");
|
||||
} else if(status == CMAuthorizationStatusRestricted) {
|
||||
ALog(@"Headphone motion restricted");
|
||||
} else if(status == CMAuthorizationStatusNotDetermined) {
|
||||
ALog(@"Headphone motion status not determined; will prompt for access");
|
||||
}
|
||||
|
||||
motionManager = [[CMHeadphoneMotionManager alloc] init];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super initWithController:c previous:p latency:latency];
|
||||
if(self) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableHrtf = [defaults boolForKey:@"enableHrtf"];
|
||||
enableHeadTracking = [defaults boolForKey:@"enableHeadTracking"];
|
||||
|
||||
rotationMatrix = matrix_identity_float4x4;
|
||||
|
||||
[self addObservers];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"HRTF dealloc");
|
||||
[self setShouldContinue:NO];
|
||||
[self cleanUp];
|
||||
[self removeObservers];
|
||||
[super cleanUp];
|
||||
if(extrapolate_buffer) {
|
||||
free(extrapolate_buffer);
|
||||
extrapolate_buffer = NULL;
|
||||
extrapolate_buffer_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addObservers {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHrtf" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPHRTFNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHeadTracking" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPHRTFNodeContext];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetReferencePosition:) name:CogPlaybackDidResetHeadTracking object:nil];
|
||||
|
||||
observersapplied = YES;
|
||||
}
|
||||
|
||||
- (void)removeObservers {
|
||||
if(observersapplied) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHrtf" context:kDSPHRTFNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHeadTracking" context:kDSPHRTFNodeContext];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:CogPlaybackDidResetHeadTracking object:nil];
|
||||
|
||||
observersapplied = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if(context != kDSPHRTFNodeContext) {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
return;
|
||||
}
|
||||
|
||||
if([keyPath isEqualToString:@"values.enableHrtf"] ||
|
||||
[keyPath isEqualToString:@"values.enableHeadTracking"]) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableHrtf = [defaults boolForKey:@"enableHrtf"];
|
||||
enableHeadTracking = [defaults boolForKey:@"enableHeadTracking"];
|
||||
resetFilter = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)fullInit {
|
||||
if(enableHrtf) {
|
||||
NSURL *presetUrl = [[NSBundle mainBundle] URLForResource:@"SADIE_D02-96000" withExtension:@"mhr"];
|
||||
|
||||
rotationMatrixUpdated = NO;
|
||||
|
||||
simd_float4x4 matrix;
|
||||
if(!referenceMatrixSet || !enableHeadTracking) {
|
||||
referenceMatrixSet = NO;
|
||||
matrix = matrix_identity_float4x4;
|
||||
self->referenceMatrix = matrix;
|
||||
if(enableHeadTracking) {
|
||||
lastEnableHeadTracking = YES;
|
||||
registerMotionListener(self);
|
||||
} else if(lastEnableHeadTracking) {
|
||||
lastEnableHeadTracking = NO;
|
||||
unregisterMotionListener();
|
||||
}
|
||||
} else {
|
||||
simd_float4x4 mirrorTransform = {
|
||||
simd_make_float4(-1.0, 0.0, 0.0, 0.0),
|
||||
simd_make_float4(0.0, 1.0, 0.0, 0.0),
|
||||
simd_make_float4(0.0, 0.0, 1.0, 0.0),
|
||||
simd_make_float4(0.0, 0.0, 0.0, 1.0)
|
||||
};
|
||||
|
||||
matrix = simd_mul(mirrorTransform, rotationMatrix);
|
||||
matrix = simd_mul(matrix, referenceMatrix);
|
||||
}
|
||||
|
||||
hrtf = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:inputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame withConfig:inputChannelConfig withMatrix:matrix];
|
||||
if(!hrtf) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
outputFormat = inputFormat;
|
||||
outputFormat.mChannelsPerFrame = 2;
|
||||
outputFormat.mBytesPerFrame = sizeof(float) * outputFormat.mChannelsPerFrame;
|
||||
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
|
||||
outputChannelConfig = AudioChannelSideLeft | AudioChannelSideRight;
|
||||
|
||||
resetFilter = NO;
|
||||
|
||||
needPrefill = [hrtf needPrefill];
|
||||
} else {
|
||||
if(lastEnableHeadTracking) {
|
||||
lastEnableHeadTracking = NO;
|
||||
unregisterMotionListener();
|
||||
}
|
||||
referenceMatrixSet = NO;
|
||||
|
||||
hrtf = nil;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)fullShutdown {
|
||||
hrtf = nil;
|
||||
if(lastEnableHeadTracking) {
|
||||
lastEnableHeadTracking = NO;
|
||||
unregisterMotionListener();
|
||||
}
|
||||
resetFilter = NO;
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
if(stopping)
|
||||
return NO;
|
||||
[self fullShutdown];
|
||||
return [self fullInit];
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
stopping = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[self fullShutdown];
|
||||
}
|
||||
|
||||
- (void)resetBuffer {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
[self fullShutdown];
|
||||
paused = NO;
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
while([self shouldContinue] == YES) {
|
||||
if(paused || endOfStream) {
|
||||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self convert];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if([previousNode endOfStream] == YES) {
|
||||
usleep(500);
|
||||
endOfStream = YES;
|
||||
continue;
|
||||
}
|
||||
if(paused) {
|
||||
continue;
|
||||
}
|
||||
usleep(500);
|
||||
} else {
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
if(resetFilter || (!enableHrtf && hrtf)) {
|
||||
[self fullShutdown];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)convert {
|
||||
if(stopping)
|
||||
return nil;
|
||||
|
||||
processEntered = YES;
|
||||
|
||||
if(stopping || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) || [self shouldContinue] == NO) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(![self peekFormat:&inputFormat channelConfig:&inputChannelConfig]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!inputFormat.mSampleRate ||
|
||||
!inputFormat.mBitsPerChannel ||
|
||||
!inputFormat.mChannelsPerFrame ||
|
||||
!inputFormat.mBytesPerFrame ||
|
||||
!inputFormat.mFramesPerPacket ||
|
||||
!inputFormat.mBytesPerPacket) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((enableHrtf && !hrtf) ||
|
||||
memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 ||
|
||||
inputChannelConfig != lastInputChannelConfig) {
|
||||
lastInputFormat = inputFormat;
|
||||
lastInputChannelConfig = inputChannelConfig;
|
||||
[self fullShutdown];
|
||||
if(enableHrtf && ![self setup]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hrtf) {
|
||||
processEntered = NO;
|
||||
return [self readChunk:4096];
|
||||
}
|
||||
|
||||
AudioChunk *chunk = [self readChunkAsFloat32:4096];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(rotationMatrixUpdated) {
|
||||
rotationMatrixUpdated = NO;
|
||||
simd_float4x4 mirrorTransform = {
|
||||
simd_make_float4(-1.0, 0.0, 0.0, 0.0),
|
||||
simd_make_float4(0.0, 1.0, 0.0, 0.0),
|
||||
simd_make_float4(0.0, 0.0, 1.0, 0.0),
|
||||
simd_make_float4(0.0, 0.0, 0.0, 1.0)
|
||||
};
|
||||
|
||||
simd_float4x4 matrix = simd_mul(mirrorTransform, rotationMatrix);
|
||||
matrix = simd_mul(matrix, referenceMatrix);
|
||||
|
||||
[hrtf reloadWithMatrix:matrix];
|
||||
}
|
||||
|
||||
double streamTimestamp = [chunk streamTimestamp];
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
if(needPrefill) {
|
||||
size_t maxToUse = 4096 - needPrefill;
|
||||
if(maxToUse > frameCount) {
|
||||
maxToUse = frameCount;
|
||||
}
|
||||
size_t channels = inputFormat.mChannelsPerFrame;
|
||||
memcpy(&prefillBuffer[needPrefill * channels], [sampleData bytes], maxToUse * sizeof(float) * channels);
|
||||
lpc_extrapolate_bkwd(&prefillBuffer[needPrefill * channels], maxToUse, maxToUse, (int)channels, LPC_ORDER, needPrefill, &extrapolate_buffer, &extrapolate_buffer_size);
|
||||
[hrtf process:&prefillBuffer[0] sampleCount:(int)needPrefill toBuffer:&outBuffer[0]];
|
||||
needPrefill = 0;
|
||||
}
|
||||
|
||||
[hrtf process:(const float *)[sampleData bytes] sampleCount:(int)frameCount toBuffer:&outBuffer[0]];
|
||||
|
||||
AudioChunk *outputChunk = [[AudioChunk alloc] init];
|
||||
[outputChunk setFormat:outputFormat];
|
||||
if(outputChannelConfig) {
|
||||
[outputChunk setChannelConfig:outputChannelConfig];
|
||||
}
|
||||
if([chunk isHDCD]) [outputChunk setHDCD];
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:[chunk streamTimeRatio]];
|
||||
[outputChunk assignSamples:&outBuffer[0] frameCount:frameCount];
|
||||
|
||||
processEntered = NO;
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
- (void)reportMotion:(simd_float4x4)matrix {
|
||||
rotationMatrix = matrix;
|
||||
if(!referenceMatrixSet) {
|
||||
referenceMatrix = simd_inverse(matrix);
|
||||
referenceMatrixSet = YES;
|
||||
}
|
||||
rotationMatrixUpdated = YES;
|
||||
}
|
||||
|
||||
- (void)resetReferencePosition:(NSNotification *)notification {
|
||||
referenceMatrixSet = NO;
|
||||
}
|
||||
|
||||
@end
|
32
Audio/Chain/DSP/DSPRubberbandNode.h
Normal file
32
Audio/Chain/DSP/DSPRubberbandNode.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// DSPRubberbandNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/10/25.
|
||||
//
|
||||
|
||||
#ifndef DSPRubberbandNode_h
|
||||
#define DSPRubberbandNode_h
|
||||
|
||||
#import <CogAudio/DSPNode.h>
|
||||
|
||||
@interface DSPRubberbandNode : DSPNode {
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (void)resetBuffer;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)process;
|
||||
- (AudioChunk * _Nullable)convert;
|
||||
|
||||
- (double)secondsBuffered;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* DSPRubberbandNode_h */
|
561
Audio/Chain/DSP/DSPRubberbandNode.m
Normal file
561
Audio/Chain/DSP/DSPRubberbandNode.m
Normal file
|
@ -0,0 +1,561 @@
|
|||
//
|
||||
// DSPRubberbandNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/10/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
#import "DSPRubberbandNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#import <rubberband/rubberband-c.h>
|
||||
|
||||
static void * kDSPRubberbandNodeContext = &kDSPRubberbandNodeContext;
|
||||
|
||||
@implementation DSPRubberbandNode {
|
||||
BOOL enableRubberband;
|
||||
|
||||
RubberBandState ts;
|
||||
RubberBandOptions tslastoptions, tsnewoptions;
|
||||
size_t tschannels;
|
||||
ssize_t blockSize, toDrop, samplesBuffered;
|
||||
BOOL tsapplynewoptions;
|
||||
BOOL tsrestartengine;
|
||||
double tempo, pitch;
|
||||
double lastTempo, lastPitch;
|
||||
double countIn;
|
||||
uint64_t countOut;
|
||||
|
||||
double streamTimestamp;
|
||||
double streamTimeRatio;
|
||||
BOOL isHDCD;
|
||||
|
||||
BOOL stopping, paused;
|
||||
BOOL processEntered;
|
||||
|
||||
BOOL flushed;
|
||||
|
||||
BOOL observersapplied;
|
||||
|
||||
AudioStreamBasicDescription lastInputFormat;
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
|
||||
uint32_t lastInputChannelConfig, inputChannelConfig;
|
||||
|
||||
float *rsPtrs[32];
|
||||
float rsInBuffer[4096 * 32];
|
||||
float rsOutBuffer[65536 * 32];
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super initWithController:c previous:p latency:latency];
|
||||
if(self) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableRubberband = ![[defaults stringForKey:@"rubberbandEngine"] isEqualToString:@"disabled"];
|
||||
|
||||
pitch = [defaults doubleForKey:@"pitch"];
|
||||
tempo = [defaults doubleForKey:@"tempo"];
|
||||
|
||||
lastPitch = pitch;
|
||||
lastTempo = tempo;
|
||||
|
||||
[self addObservers];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"Rubber Band dealloc");
|
||||
[self setShouldContinue:NO];
|
||||
[self cleanUp];
|
||||
[self removeObservers];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
- (void)addObservers {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.pitch" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.tempo" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandEngine" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandTransients" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandDetector" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandPhase" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandWindow" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandSmoothing" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandFormant" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandPitch" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandChannels" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kDSPRubberbandNodeContext];
|
||||
|
||||
observersapplied = YES;
|
||||
}
|
||||
|
||||
- (void)removeObservers {
|
||||
if(observersapplied) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.pitch" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.tempo" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandEngine" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandTransients" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandDetector" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandPhase" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandWindow" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandSmoothing" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandFormant" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandPitch" context:kDSPRubberbandNodeContext];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandChannels" context:kDSPRubberbandNodeContext];
|
||||
observersapplied = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if(context != kDSPRubberbandNodeContext) {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
return;
|
||||
}
|
||||
|
||||
if([keyPath isEqualToString:@"values.pitch"] ||
|
||||
[keyPath isEqualToString:@"values.tempo"]) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
pitch = [defaults doubleForKey:@"pitch"];
|
||||
tempo = [defaults doubleForKey:@"tempo"];
|
||||
tsapplynewoptions = YES;
|
||||
} else if([[keyPath substringToIndex:17] isEqualToString:@"values.rubberband"]) {
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
enableRubberband = ![[defaults stringForKey:@"rubberbandEngine"] isEqualToString:@"disabled"];
|
||||
if(enableRubberband && ts) {
|
||||
RubberBandOptions options = [self getRubberbandOptions];
|
||||
RubberBandOptions changed = options ^ tslastoptions;
|
||||
if(changed) {
|
||||
BOOL engineR3 = !!(options & RubberBandOptionEngineFiner);
|
||||
// Options which require a restart of the engine
|
||||
const RubberBandOptions mustRestart = RubberBandOptionEngineFaster | RubberBandOptionEngineFiner | RubberBandOptionWindowStandard | RubberBandOptionWindowShort | RubberBandOptionWindowLong | RubberBandOptionSmoothingOff | RubberBandOptionSmoothingOn | (engineR3 ? RubberBandOptionPitchHighSpeed | RubberBandOptionPitchHighQuality | RubberBandOptionPitchHighConsistency : 0) | RubberBandOptionChannelsApart | RubberBandOptionChannelsTogether;
|
||||
if(changed & mustRestart) {
|
||||
tsrestartengine = YES;
|
||||
} else {
|
||||
tsnewoptions = options;
|
||||
tsapplynewoptions = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (RubberBandOptions)getRubberbandOptions {
|
||||
RubberBandOptions options = RubberBandOptionProcessRealTime;
|
||||
|
||||
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
|
||||
NSString *value = [defaults stringForKey:@"rubberbandEngine"];
|
||||
BOOL engineR3 = NO;
|
||||
if([value isEqualToString:@"faster"]) {
|
||||
options |= RubberBandOptionEngineFaster;
|
||||
} else if([value isEqualToString:@"finer"]) {
|
||||
options |= RubberBandOptionEngineFiner;
|
||||
engineR3 = YES;
|
||||
}
|
||||
|
||||
if(!engineR3) {
|
||||
value = [defaults stringForKey:@"rubberbandTransients"];
|
||||
if([value isEqualToString:@"crisp"]) {
|
||||
options |= RubberBandOptionTransientsCrisp;
|
||||
} else if([value isEqualToString:@"mixed"]) {
|
||||
options |= RubberBandOptionTransientsMixed;
|
||||
} else if([value isEqualToString:@"smooth"]) {
|
||||
options |= RubberBandOptionTransientsSmooth;
|
||||
}
|
||||
|
||||
value = [defaults stringForKey:@"rubberbandDetector"];
|
||||
if([value isEqualToString:@"compound"]) {
|
||||
options |= RubberBandOptionDetectorCompound;
|
||||
} else if([value isEqualToString:@"percussive"]) {
|
||||
options |= RubberBandOptionDetectorPercussive;
|
||||
} else if([value isEqualToString:@"soft"]) {
|
||||
options |= RubberBandOptionDetectorSoft;
|
||||
}
|
||||
|
||||
value = [defaults stringForKey:@"rubberbandPhase"];
|
||||
if([value isEqualToString:@"laminar"]) {
|
||||
options |= RubberBandOptionPhaseLaminar;
|
||||
} else if([value isEqualToString:@"independent"]) {
|
||||
options |= RubberBandOptionPhaseIndependent;
|
||||
}
|
||||
}
|
||||
|
||||
value = [defaults stringForKey:@"rubberbandWindow"];
|
||||
if([value isEqualToString:@"standard"]) {
|
||||
options |= RubberBandOptionWindowStandard;
|
||||
} else if([value isEqualToString:@"short"]) {
|
||||
options |= RubberBandOptionWindowShort;
|
||||
} else if([value isEqualToString:@"long"]) {
|
||||
if(engineR3) {
|
||||
options |= RubberBandOptionWindowStandard;
|
||||
} else {
|
||||
options |= RubberBandOptionWindowLong;
|
||||
}
|
||||
}
|
||||
|
||||
if(!engineR3) {
|
||||
value = [defaults stringForKey:@"rubberbandSmoothing"];
|
||||
if([value isEqualToString:@"off"]) {
|
||||
options |= RubberBandOptionSmoothingOff;
|
||||
} else if([value isEqualToString:@"on"]) {
|
||||
options |= RubberBandOptionSmoothingOn;
|
||||
}
|
||||
}
|
||||
|
||||
value = [defaults stringForKey:@"rubberbandFormant"];
|
||||
if([value isEqualToString:@"shifted"]) {
|
||||
options |= RubberBandOptionFormantShifted;
|
||||
} else if([value isEqualToString:@"preserved"]) {
|
||||
options |= RubberBandOptionFormantPreserved;
|
||||
}
|
||||
|
||||
value = [defaults stringForKey:@"rubberbandPitch"];
|
||||
if([value isEqualToString:@"highspeed"]) {
|
||||
options |= RubberBandOptionPitchHighSpeed;
|
||||
} else if([value isEqualToString:@"highquality"]) {
|
||||
options |= RubberBandOptionPitchHighQuality;
|
||||
} else if([value isEqualToString:@"highconsistency"]) {
|
||||
options |= RubberBandOptionPitchHighConsistency;
|
||||
}
|
||||
|
||||
value = [defaults stringForKey:@"rubberbandChannels"];
|
||||
if([value isEqualToString:@"apart"]) {
|
||||
options |= RubberBandOptionChannelsApart;
|
||||
} else if([value isEqualToString:@"together"]) {
|
||||
options |= RubberBandOptionChannelsTogether;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
- (BOOL)fullInit {
|
||||
RubberBandOptions options = [self getRubberbandOptions];
|
||||
tslastoptions = options;
|
||||
tschannels = inputFormat.mChannelsPerFrame;
|
||||
ts = rubberband_new(inputFormat.mSampleRate, (int)tschannels, options, 1.0 / tempo, pitch);
|
||||
if(!ts)
|
||||
return NO;
|
||||
|
||||
blockSize = rubberband_get_process_size_limit(ts);
|
||||
toDrop = rubberband_get_start_delay(ts);
|
||||
samplesBuffered = 0;
|
||||
if(blockSize > 4096)
|
||||
blockSize = 4096;
|
||||
rubberband_set_max_process_size(ts, (unsigned int)blockSize);
|
||||
|
||||
for(size_t i = 0; i < 32; ++i) {
|
||||
rsPtrs[i] = &rsInBuffer[4096 * i];
|
||||
}
|
||||
|
||||
ssize_t toPad = rubberband_get_preferred_start_pad(ts);
|
||||
if(toPad > 0) {
|
||||
for(size_t i = 0; i < tschannels; ++i) {
|
||||
memset(rsPtrs[i], 0, 4096 * sizeof(float));
|
||||
}
|
||||
while(toPad > 0) {
|
||||
ssize_t p = toPad;
|
||||
if(p > blockSize) p = blockSize;
|
||||
rubberband_process(ts, (const float * const *)rsPtrs, (int)p, false);
|
||||
toPad -= p;
|
||||
}
|
||||
}
|
||||
|
||||
tsapplynewoptions = NO;
|
||||
tsrestartengine = NO;
|
||||
flushed = NO;
|
||||
|
||||
countIn = 0.0;
|
||||
countOut = 0;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)partialInit {
|
||||
if(stopping || paused || !ts) return;
|
||||
|
||||
processEntered = YES;
|
||||
|
||||
RubberBandOptions changed = tslastoptions ^ tsnewoptions;
|
||||
|
||||
if(changed) {
|
||||
tslastoptions = tsnewoptions;
|
||||
|
||||
BOOL engineR3 = !!(tsnewoptions & RubberBandOptionEngineFiner);
|
||||
const RubberBandOptions transientsmask = RubberBandOptionTransientsCrisp | RubberBandOptionTransientsMixed | RubberBandOptionTransientsSmooth;
|
||||
const RubberBandOptions detectormask = RubberBandOptionDetectorCompound | RubberBandOptionDetectorPercussive | RubberBandOptionDetectorSoft;
|
||||
const RubberBandOptions phasemask = RubberBandOptionPhaseLaminar | RubberBandOptionPhaseIndependent;
|
||||
const RubberBandOptions formantmask = RubberBandOptionFormantShifted | RubberBandOptionFormantPreserved;
|
||||
const RubberBandOptions pitchmask = RubberBandOptionPitchHighSpeed | RubberBandOptionPitchHighQuality | RubberBandOptionPitchHighConsistency;
|
||||
if(changed & transientsmask)
|
||||
rubberband_set_transients_option(ts, tsnewoptions & transientsmask);
|
||||
if(!engineR3) {
|
||||
if(changed & detectormask)
|
||||
rubberband_set_detector_option(ts, tsnewoptions & detectormask);
|
||||
if(changed & phasemask)
|
||||
rubberband_set_phase_option(ts, tsnewoptions & phasemask);
|
||||
}
|
||||
if(changed & formantmask)
|
||||
rubberband_set_formant_option(ts, tsnewoptions & formantmask);
|
||||
if(!engineR3 && (changed & pitchmask))
|
||||
rubberband_set_pitch_option(ts, tsnewoptions & pitchmask);
|
||||
}
|
||||
|
||||
if(fabs(pitch - lastPitch) > 1e-5 ||
|
||||
fabs(tempo - lastTempo) > 1e-5) {
|
||||
lastPitch = pitch;
|
||||
lastTempo = tempo;
|
||||
rubberband_set_pitch_scale(ts, pitch);
|
||||
rubberband_set_time_ratio(ts, 1.0 / tempo);
|
||||
}
|
||||
|
||||
tsapplynewoptions = NO;
|
||||
|
||||
processEntered = NO;
|
||||
}
|
||||
|
||||
- (void)fullShutdown {
|
||||
if(ts) {
|
||||
rubberband_delete(ts);
|
||||
ts = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
if(stopping)
|
||||
return NO;
|
||||
[self fullShutdown];
|
||||
return [self fullInit];
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
stopping = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[self fullShutdown];
|
||||
}
|
||||
|
||||
- (void)resetBuffer {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
[self fullShutdown];
|
||||
paused = NO;
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)setPreviousNode:(id)p {
|
||||
if(previousNode != p) {
|
||||
paused = YES;
|
||||
while(processEntered);
|
||||
previousNode = p;
|
||||
paused = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setEndOfStream:(BOOL)e {
|
||||
if(endOfStream && !e) {
|
||||
while(processEntered);
|
||||
[self fullShutdown];
|
||||
}
|
||||
[super setEndOfStream:e];
|
||||
flushed = e;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
while([self shouldContinue] == YES) {
|
||||
if(paused || endOfStream) {
|
||||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self convert];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if(!ts) {
|
||||
flushed = previousNode && [[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES;
|
||||
}
|
||||
if(flushed) {
|
||||
usleep(500);
|
||||
endOfStream = YES;
|
||||
continue;
|
||||
}
|
||||
if(paused) {
|
||||
continue;
|
||||
}
|
||||
usleep(500);
|
||||
} else {
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
if(!enableRubberband && ts) {
|
||||
[self fullShutdown];
|
||||
} else if(tsrestartengine) {
|
||||
[self fullShutdown];
|
||||
} else if(tsapplynewoptions) {
|
||||
[self partialInit];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)convert {
|
||||
if(stopping)
|
||||
return nil;
|
||||
|
||||
processEntered = YES;
|
||||
|
||||
if(stopping || flushed || !previousNode || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) || [self shouldContinue] == NO) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(![self peekFormat:&inputFormat channelConfig:&inputChannelConfig]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!inputFormat.mSampleRate ||
|
||||
!inputFormat.mBitsPerChannel ||
|
||||
!inputFormat.mChannelsPerFrame ||
|
||||
!inputFormat.mBytesPerFrame ||
|
||||
!inputFormat.mFramesPerPacket ||
|
||||
!inputFormat.mBytesPerPacket) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if((enableRubberband && !ts) ||
|
||||
memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 ||
|
||||
inputChannelConfig != lastInputChannelConfig) {
|
||||
lastInputFormat = inputFormat;
|
||||
lastInputChannelConfig = inputChannelConfig;
|
||||
[self fullShutdown];
|
||||
if(enableRubberband && ![self setup]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ts) {
|
||||
processEntered = NO;
|
||||
return [self readChunk:4096];
|
||||
}
|
||||
|
||||
ssize_t samplesToProcess = rubberband_get_samples_required(ts);
|
||||
if(samplesToProcess > blockSize)
|
||||
samplesToProcess = blockSize;
|
||||
|
||||
int channels = (int)(inputFormat.mChannelsPerFrame);
|
||||
|
||||
if(samplesToProcess > 0) {
|
||||
AudioChunk *chunk = [self readAndMergeChunksAsFloat32:samplesToProcess];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
processEntered = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
streamTimestamp = [chunk streamTimestamp];
|
||||
streamTimeRatio = [chunk streamTimeRatio];
|
||||
isHDCD = [chunk isHDCD];
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
countIn += ((double)frameCount) / tempo;
|
||||
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
for (size_t i = 0; i < channels; ++i) {
|
||||
cblas_scopy((int)frameCount, ((const float *)[sampleData bytes]) + i, channels, rsPtrs[i], 1);
|
||||
}
|
||||
|
||||
flushed = [[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES;
|
||||
|
||||
int len = (int)frameCount;
|
||||
|
||||
rubberband_process(ts, (const float * const *)rsPtrs, len, flushed);
|
||||
}
|
||||
|
||||
ssize_t samplesAvailable;
|
||||
while(!stopping && (samplesAvailable = rubberband_available(ts)) > 0) {
|
||||
if(toDrop > 0) {
|
||||
ssize_t blockDrop = toDrop;
|
||||
if(blockDrop > samplesAvailable) blockDrop = samplesAvailable;
|
||||
if(blockDrop > blockSize) blockDrop = blockSize;
|
||||
rubberband_retrieve(ts, (float * const *)rsPtrs, (int)blockDrop);
|
||||
toDrop -= blockDrop;
|
||||
continue;
|
||||
}
|
||||
ssize_t maxAvailable = 65536 - samplesBuffered;
|
||||
ssize_t samplesOut = samplesAvailable;
|
||||
if(samplesOut > maxAvailable) {
|
||||
samplesOut = maxAvailable;
|
||||
if(samplesOut <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(samplesOut > blockSize) samplesOut = blockSize;
|
||||
rubberband_retrieve(ts, (float * const *)rsPtrs, (int)samplesOut);
|
||||
for(size_t i = 0; i < channels; ++i) {
|
||||
cblas_scopy((int)samplesOut, rsPtrs[i], 1, &rsOutBuffer[samplesBuffered * channels + i], channels);
|
||||
}
|
||||
samplesBuffered += samplesOut;
|
||||
}
|
||||
|
||||
if(flushed) {
|
||||
if(samplesBuffered > 0) {
|
||||
ssize_t ideal = (ssize_t)floor(countIn + 0.5);
|
||||
if(countOut + samplesBuffered > ideal) {
|
||||
// Rubber Band does not account for flushing duration in real time mode
|
||||
samplesBuffered = ideal - countOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioChunk *outputChunk = nil;
|
||||
if(samplesBuffered > 0) {
|
||||
outputChunk = [[AudioChunk alloc] init];
|
||||
[outputChunk setFormat:inputFormat];
|
||||
if(inputChannelConfig) {
|
||||
[outputChunk setChannelConfig:inputChannelConfig];
|
||||
}
|
||||
if(isHDCD) [outputChunk setHDCD];
|
||||
[outputChunk setStreamTimestamp:streamTimestamp];
|
||||
[outputChunk setStreamTimeRatio:streamTimeRatio * tempo];
|
||||
[outputChunk assignSamples:&rsOutBuffer[0] frameCount:samplesBuffered];
|
||||
countOut += samplesBuffered;
|
||||
samplesBuffered = 0;
|
||||
double chunkDuration = [outputChunk duration];
|
||||
streamTimestamp += chunkDuration * [outputChunk streamTimeRatio];
|
||||
}
|
||||
|
||||
processEntered = NO;
|
||||
return outputChunk;
|
||||
}
|
||||
|
||||
- (double)secondsBuffered {
|
||||
double rbBuffered = 0.0;
|
||||
if(ts) {
|
||||
// We don't use Rubber Band's latency function, because at least in Cog's case,
|
||||
// by the time we call this function, and also, because it doesn't account for
|
||||
// how much audio will be lopped off at the end of the process.
|
||||
//
|
||||
// Tested once, this tends to be close to zero when actually called.
|
||||
rbBuffered = countIn - (double)(countOut);
|
||||
if(rbBuffered < 0) {
|
||||
rbBuffered = 0.0;
|
||||
} else {
|
||||
rbBuffered /= inputFormat.mSampleRate;
|
||||
}
|
||||
}
|
||||
return [buffer listDuration] + rbBuffered;
|
||||
}
|
||||
|
||||
@end
|
|
@ -157,6 +157,8 @@ static void downmix_to_mono(const float *inBuffer, int channels, uint32_t config
|
|||
}
|
||||
cblas_scopy((int)count, inBuffer, 2, outBuffer, 1);
|
||||
vDSP_vadd(outBuffer, 1, inBuffer + 1, 2, outBuffer, 1, count);
|
||||
const float scale = 0.5f;
|
||||
vDSP_vsmul(outBuffer, 1, &scale, outBuffer, 1, count);
|
||||
}
|
||||
|
||||
static void upmix(const float *inBuffer, int inchannels, uint32_t inconfig, float *outBuffer, int outchannels, uint32_t outconfig, size_t count) {
|
||||
|
@ -283,7 +285,11 @@ static void *kDownmixProcessorContext = &kDownmixProcessorContext;
|
|||
}
|
||||
|
||||
- (void)process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer {
|
||||
if(inputFormat.mChannelsPerFrame > 2 && outConfig == AudioConfigStereo) {
|
||||
if(inputFormat.mChannelsPerFrame == 2 && outConfig == AudioConfigStereo &&
|
||||
inConfig == (AudioChannelSideLeft | AudioChannelSideRight)) {
|
||||
// Workaround for HRTF output
|
||||
memcpy(outBuffer, inBuffer, frames * outputFormat.mBytesPerPacket);
|
||||
} else if(inputFormat.mChannelsPerFrame > 2 && outConfig == AudioConfigStereo) {
|
||||
downmix_to_stereo((const float *)inBuffer, inputFormat.mChannelsPerFrame, inConfig, (float *)outBuffer, frames);
|
||||
} else if(inputFormat.mChannelsPerFrame > 1 && outConfig == AudioConfigMono) {
|
||||
downmix_to_mono((const float *)inBuffer, inputFormat.mChannelsPerFrame, inConfig, (float *)outBuffer, frames);
|
|
@ -11,37 +11,36 @@
|
|||
#import <Accelerate/Accelerate.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface HeadphoneFilter : NSObject {
|
||||
vDSP_DFT_Setup dftSetupF;
|
||||
vDSP_DFT_Setup dftSetupB;
|
||||
#import <simd/simd.h>
|
||||
|
||||
@interface HeadphoneFilter : NSObject {
|
||||
NSURL *URL;
|
||||
|
||||
int fftSize;
|
||||
int fftSizeOver2;
|
||||
int bufferSize;
|
||||
int paddedBufferSize;
|
||||
double sampleRate;
|
||||
int channelCount;
|
||||
uint32_t config;
|
||||
|
||||
DSPSplitComplex signal_fft;
|
||||
DSPSplitComplex input_filtered_signal_per_channel[2];
|
||||
DSPSplitComplex input_filtered_signal_totals[2];
|
||||
DSPSplitComplex *impulse_responses;
|
||||
|
||||
float **mirroredImpulseResponses;
|
||||
|
||||
float **prevInputs;
|
||||
|
||||
float *left_result;
|
||||
float *right_result;
|
||||
|
||||
float *paddedSignal;
|
||||
float *paddedSignal[2];
|
||||
}
|
||||
|
||||
+ (BOOL)validateImpulseFile:(NSURL *)url;
|
||||
|
||||
- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(int)channels withConfig:(uint32_t)config;
|
||||
- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(int)channels withConfig:(uint32_t)config withMatrix:(simd_float4x4)matrix;
|
||||
|
||||
- (void)reloadWithMatrix:(simd_float4x4)matrix;
|
||||
|
||||
- (void)process:(const float *)inBuffer sampleCount:(int)count toBuffer:(float *)outBuffer;
|
||||
|
||||
- (void)reset;
|
||||
|
||||
- (size_t)needPrefill;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* HeadphoneFilter_h */
|
386
Audio/Chain/DSP/HeadphoneFilter.mm
Normal file
386
Audio/Chain/DSP/HeadphoneFilter.mm
Normal file
|
@ -0,0 +1,386 @@
|
|||
//
|
||||
// HeadphoneFilter.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 1/24/22.
|
||||
//
|
||||
|
||||
#import "HeadphoneFilter.h"
|
||||
#import "AudioChunk.h"
|
||||
#import "AudioDecoder.h"
|
||||
#import "AudioSource.h"
|
||||
|
||||
#import <stdlib.h>
|
||||
|
||||
#import <fstream>
|
||||
|
||||
#import <soxr.h>
|
||||
|
||||
#import "HrtfData.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
typedef struct speakerPosition {
|
||||
float elevation;
|
||||
float azimuth;
|
||||
float distance;
|
||||
} speakerPosition;
|
||||
|
||||
#define DEGREES(x) ((x)*M_PI / 180.0)
|
||||
|
||||
static const speakerPosition speakerPositions[18] = {
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-135.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+135.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-15.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+15.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-180.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-90.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+90.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+90.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(-30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(+30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(-135.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(+135.0), .distance = 1.0 }
|
||||
};
|
||||
|
||||
static simd_float4x4 matX(float theta) {
|
||||
simd_float4x4 mat = {
|
||||
simd_make_float4(1.0f, 0.0f, 0.0f, 0.0f),
|
||||
simd_make_float4(0.0f, cosf(theta), -sinf(theta), 0.0f),
|
||||
simd_make_float4(0.0f, sinf(theta), cosf(theta), 0.0f),
|
||||
simd_make_float4(0.0f, 0.0f, 0.0f, 1.0f)
|
||||
};
|
||||
return mat;
|
||||
};
|
||||
|
||||
static simd_float4x4 matY(float theta) {
|
||||
simd_float4x4 mat = {
|
||||
simd_make_float4(cosf(theta), 0.0f, sinf(theta), 0.0f),
|
||||
simd_make_float4(0.0f, 1.0f, 0.0f, 0.0f),
|
||||
simd_make_float4(-sinf(theta), 0.0f, cosf(theta), 0.0f),
|
||||
simd_make_float4(0.0f, 0.0f, 0.0f, 1.0f)
|
||||
};
|
||||
return mat;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static simd_float4x4 matZ(float theta) {
|
||||
simd_float4x4 mat = {
|
||||
simd_make_float4(cosf(theta), -sinf(theta), 0.0f, 0.0f),
|
||||
simd_make_float4(sinf(theta), cosf(theta), 0.0f, 0.0f),
|
||||
simd_make_float4(0.0f, 0.0f, 1.0f, 0.0f),
|
||||
simd_make_float4(0.0f, 0.0f, 0.0f, 1.0f)
|
||||
};
|
||||
return mat;
|
||||
};
|
||||
#endif
|
||||
|
||||
static void transformPosition(float &elevation, float &azimuth, const simd_float4x4 &matrix) {
|
||||
simd_float4x4 mat_x = matX(azimuth);
|
||||
simd_float4x4 mat_y = matY(elevation);
|
||||
//simd_float4x4 mat_z = matrix_identity_float4x4;
|
||||
simd_float4x4 offsetMatrix = simd_mul(mat_x, mat_y);
|
||||
//offsetMatrix = simd_mul(offsetMatrix, mat_z);
|
||||
offsetMatrix = simd_mul(offsetMatrix, matrix);
|
||||
|
||||
double sy = sqrt(offsetMatrix.columns[0].x * offsetMatrix.columns[0].x + offsetMatrix.columns[1].x * offsetMatrix.columns[1].x);
|
||||
|
||||
bool singular = sy < 1e-6; // If
|
||||
|
||||
float x, y/*, z*/;
|
||||
if(!singular) {
|
||||
x = atan2(offsetMatrix.columns[2].y, offsetMatrix.columns[2].z);
|
||||
y = atan2(-offsetMatrix.columns[2].x, sy);
|
||||
//z = atan2(offsetMatrix.columns[1].x, offsetMatrix.columns[0].x);
|
||||
} else {
|
||||
x = atan2(-offsetMatrix.columns[1].z, offsetMatrix.columns[1].y);
|
||||
y = atan2(-offsetMatrix.columns[2].x, sy);
|
||||
//z = 0;
|
||||
}
|
||||
|
||||
elevation = y;
|
||||
azimuth = x;
|
||||
|
||||
if(elevation < (M_PI * (-0.5))) {
|
||||
elevation = (M_PI * (-0.5));
|
||||
} else if(elevation > M_PI * 0.5) {
|
||||
elevation = M_PI * 0.5;
|
||||
}
|
||||
while(azimuth < (M_PI * (-2.0))) {
|
||||
azimuth += M_PI * 2.0;
|
||||
}
|
||||
while(azimuth > M_PI * 2.0) {
|
||||
azimuth -= M_PI * 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
@interface impulseSetCache : NSObject {
|
||||
NSURL *URL;
|
||||
HrtfData *data;
|
||||
}
|
||||
+ (impulseSetCache *)sharedController;
|
||||
- (void)getImpulse:(NSURL *)url outImpulse:(float **)outImpulse outSampleCount:(int *)outSampleCount sampleRate:(double)sampleRate channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig withMatrix:(simd_float4x4)matrix;
|
||||
@end
|
||||
|
||||
@implementation impulseSetCache
|
||||
static impulseSetCache *_sharedController = nil;
|
||||
|
||||
+ (impulseSetCache *)sharedController {
|
||||
@synchronized(self) {
|
||||
if(!_sharedController) {
|
||||
_sharedController = [[impulseSetCache alloc] init];
|
||||
}
|
||||
}
|
||||
return _sharedController;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
data = NULL;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
delete data;
|
||||
}
|
||||
|
||||
- (void)getImpulse:(NSURL *)url outImpulse:(float **)outImpulse outSampleCount:(int *)outSampleCount sampleRate:(double)sampleRate channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig withMatrix:(simd_float4x4)matrix {
|
||||
double sampleRateOfSource = 0;
|
||||
int sampleCount = 0;
|
||||
|
||||
if(!data || ![url isEqualTo:URL]) {
|
||||
delete data;
|
||||
data = NULL;
|
||||
URL = url;
|
||||
NSString *filePath = [url path];
|
||||
try {
|
||||
std::ifstream file([filePath UTF8String], std::fstream::binary);
|
||||
if(!file.is_open()) {
|
||||
throw std::logic_error("Cannot open file.");
|
||||
}
|
||||
data = new HrtfData(file);
|
||||
file.close();
|
||||
} catch(std::exception &e) {
|
||||
ALog(@"Exception caught: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, 0);
|
||||
soxr_io_spec_t io_spec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
|
||||
soxr_runtime_spec_t runtime_spec = soxr_runtime_spec(0);
|
||||
|
||||
bool resampling;
|
||||
|
||||
sampleRateOfSource = data->get_sample_rate();
|
||||
resampling = !!(fabs(sampleRateOfSource - sampleRate) > 1e-6);
|
||||
|
||||
uint32_t sampleCountResampled;
|
||||
uint32_t sampleCountExact = data->get_response_length();
|
||||
sampleCount = sampleCountExact + ((data->get_longest_delay() + 2) >> 2);
|
||||
|
||||
uint32_t actualSampleCount = sampleCount;
|
||||
if(resampling) {
|
||||
sampleCountResampled = (uint32_t)(((double)sampleCountExact) * sampleRate / sampleRateOfSource);
|
||||
actualSampleCount = (uint32_t)(((double)actualSampleCount) * sampleRate / sampleRateOfSource);
|
||||
io_spec.scale = sampleRateOfSource / sampleRate;
|
||||
}
|
||||
actualSampleCount = (actualSampleCount + 15) & ~15;
|
||||
|
||||
*outImpulse = (float *)calloc(sizeof(float), actualSampleCount * channelCount * 2);
|
||||
if(!*outImpulse) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
float *hrtfData = *outImpulse;
|
||||
|
||||
for(uint32_t i = 0; i < channelCount; ++i) {
|
||||
uint32_t channelFlag = [AudioChunk extractChannelFlag:i fromConfig:channelConfig];
|
||||
uint32_t channelNumber = [AudioChunk findChannelIndex:channelFlag];
|
||||
|
||||
if(channelNumber < 18) {
|
||||
const speakerPosition &speaker = speakerPositions[channelNumber];
|
||||
DirectionData hrtfLeft;
|
||||
DirectionData hrtfRight;
|
||||
|
||||
float azimuth = speaker.azimuth;
|
||||
float elevation = speaker.elevation;
|
||||
|
||||
transformPosition(elevation, azimuth, matrix);
|
||||
|
||||
data->get_direction_data(elevation, azimuth, speaker.distance, hrtfLeft, hrtfRight);
|
||||
|
||||
if(resampling) {
|
||||
ssize_t leftDelay = (ssize_t)((double)(hrtfLeft.delay) * 0.25 * sampleRate / sampleRateOfSource);
|
||||
ssize_t rightDelay = (ssize_t)((double)(hrtfRight.delay) * 0.25 * sampleRate / sampleRateOfSource);
|
||||
soxr_oneshot(sampleRateOfSource, sampleRate, 1, &hrtfLeft.impulse_response[0], sampleCountExact, NULL, &hrtfData[leftDelay + actualSampleCount * i * 2], sampleCountResampled, NULL, &io_spec, &q_spec, &runtime_spec);
|
||||
soxr_oneshot(sampleRateOfSource, sampleRate, 1, &hrtfRight.impulse_response[0], sampleCountExact, NULL, &hrtfData[rightDelay + actualSampleCount * (i * 2 + 1)], sampleCountResampled, NULL, &io_spec, &q_spec, &runtime_spec);
|
||||
} else {
|
||||
cblas_scopy(sampleCountExact, &hrtfLeft.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) + actualSampleCount * i * 2], 1);
|
||||
cblas_scopy(sampleCountExact, &hrtfRight.impulse_response[0], 1, &hrtfData[((hrtfRight.delay + 2) >> 2) + actualSampleCount * (i * 2 + 1)], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*outSampleCount = actualSampleCount;
|
||||
} catch(std::exception &e) {
|
||||
ALog(@"Exception caught: %s", e.what());
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation HeadphoneFilter
|
||||
|
||||
+ (BOOL)validateImpulseFile:(NSURL *)url {
|
||||
NSString *filePath = [url path];
|
||||
|
||||
try {
|
||||
std::ifstream file([filePath UTF8String], std::fstream::binary);
|
||||
|
||||
if(!file.is_open()) {
|
||||
throw std::logic_error("Cannot open file.");
|
||||
}
|
||||
|
||||
HrtfData data(file);
|
||||
|
||||
file.close();
|
||||
|
||||
return YES;
|
||||
} catch(std::exception &e) {
|
||||
ALog(@"Exception thrown: %s", e.what());
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(int)channels withConfig:(uint32_t)config withMatrix:(simd_float4x4)matrix {
|
||||
self = [super init];
|
||||
|
||||
if(self) {
|
||||
URL = url;
|
||||
self->sampleRate = sampleRate;
|
||||
channelCount = channels;
|
||||
self->config = config;
|
||||
|
||||
float *impulseBuffer = NULL;
|
||||
int sampleCount = 0;
|
||||
[[impulseSetCache sharedController] getImpulse:url outImpulse:&impulseBuffer outSampleCount:&sampleCount sampleRate:sampleRate channelCount:channels channelConfig:config withMatrix:matrix];
|
||||
if(!impulseBuffer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
mirroredImpulseResponses = (float **)calloc(sizeof(float *), channelCount * 2);
|
||||
if(!mirroredImpulseResponses) {
|
||||
free(impulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
for(int i = 0; i < channelCount * 2; ++i) {
|
||||
mirroredImpulseResponses[i] = &impulseBuffer[sampleCount * i];
|
||||
vDSP_vrvrs(mirroredImpulseResponses[i], 1, sampleCount);
|
||||
}
|
||||
|
||||
paddedBufferSize = sampleCount;
|
||||
|
||||
paddedSignal[0] = (float *)calloc(sizeof(float), paddedBufferSize * 2);
|
||||
if(!paddedSignal[0]) {
|
||||
return nil;
|
||||
}
|
||||
paddedSignal[1] = paddedSignal[0] + paddedBufferSize;
|
||||
|
||||
prevInputs = (float **)calloc(channels, sizeof(float *));
|
||||
if(!prevInputs)
|
||||
return nil;
|
||||
prevInputs[0] = (float *)calloc(sizeof(float), sampleCount * channelCount);
|
||||
if(!prevInputs[0])
|
||||
return nil;
|
||||
for(int i = 1; i < channels; ++i) {
|
||||
prevInputs[i] = prevInputs[i - 1] + sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if(paddedSignal[0]) {
|
||||
free(paddedSignal[0]);
|
||||
}
|
||||
|
||||
if(prevInputs) {
|
||||
if(prevInputs[0]) {
|
||||
free(prevInputs[0]);
|
||||
}
|
||||
free(prevInputs);
|
||||
}
|
||||
|
||||
if(mirroredImpulseResponses) {
|
||||
if(mirroredImpulseResponses[0]) {
|
||||
free(mirroredImpulseResponses[0]);
|
||||
}
|
||||
free(mirroredImpulseResponses);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadWithMatrix:(simd_float4x4)matrix {
|
||||
@synchronized (self) {
|
||||
if(!mirroredImpulseResponses[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
free(mirroredImpulseResponses[0]);
|
||||
|
||||
float *impulseBuffer = NULL;
|
||||
int sampleCount = 0;
|
||||
[[impulseSetCache sharedController] getImpulse:URL outImpulse:&impulseBuffer outSampleCount:&sampleCount sampleRate:sampleRate channelCount:channelCount channelConfig:config withMatrix:matrix];
|
||||
|
||||
for(int i = 0; i < channelCount * 2; ++i) {
|
||||
mirroredImpulseResponses[i] = &impulseBuffer[sampleCount * i];
|
||||
vDSP_vrvrs(mirroredImpulseResponses[i], 1, sampleCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)process:(const float *)inBuffer sampleCount:(int)count toBuffer:(float *)outBuffer {
|
||||
@synchronized (self) {
|
||||
int sampleCount = paddedBufferSize;
|
||||
while(count > 0) {
|
||||
float left = 0, right = 0;
|
||||
for(int i = 0; i < channelCount; ++i) {
|
||||
float thisleft, thisright;
|
||||
vDSP_vmul(prevInputs[i], 1, mirroredImpulseResponses[i * 2], 1, paddedSignal[0], 1, sampleCount);
|
||||
vDSP_vmul(prevInputs[i], 1, mirroredImpulseResponses[i * 2 + 1], 1, paddedSignal[1], 1, sampleCount);
|
||||
vDSP_sve(paddedSignal[0], 1, &thisleft, sampleCount);
|
||||
vDSP_sve(paddedSignal[1], 1, &thisright, sampleCount);
|
||||
left += thisleft;
|
||||
right += thisright;
|
||||
|
||||
memmove(prevInputs[i], prevInputs[i] + 1, sizeof(float) * (sampleCount - 1));
|
||||
prevInputs[i][sampleCount - 1] = *inBuffer++;
|
||||
}
|
||||
|
||||
outBuffer[0] = left;
|
||||
outBuffer[1] = right;
|
||||
outBuffer += 2;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
for(int i = 0; i < channelCount; ++i) {
|
||||
vDSP_vclr(prevInputs[i], 1, paddedBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
- (size_t)needPrefill {
|
||||
return paddedBufferSize;
|
||||
}
|
||||
|
||||
@end
|
26
Audio/Chain/DSPNode.h
Normal file
26
Audio/Chain/DSPNode.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// DSPNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/10/25.
|
||||
//
|
||||
|
||||
#ifndef DSPNode_h
|
||||
#define DSPNode_h
|
||||
|
||||
#import <CogAudio/Node.h>
|
||||
|
||||
@interface DSPNode : Node {
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (void)threadEntry:(id _Nullable)arg;
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s;
|
||||
|
||||
- (double)secondsBuffered;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* DSPNode_h */
|
76
Audio/Chain/DSPNode.m
Normal file
76
Audio/Chain/DSPNode.m
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// DSPNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/10/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "DSPNode.h"
|
||||
|
||||
@implementation DSPNode {
|
||||
BOOL threadTerminated;
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:latency];
|
||||
|
||||
writeSemaphore = [[Semaphore alloc] init];
|
||||
readSemaphore = [[Semaphore alloc] init];
|
||||
|
||||
accessLock = [[NSLock alloc] init];
|
||||
|
||||
initialBufferFilled = NO;
|
||||
|
||||
controller = c;
|
||||
endOfStream = NO;
|
||||
shouldContinue = YES;
|
||||
|
||||
nodeChannelConfig = 0;
|
||||
nodeLossless = NO;
|
||||
|
||||
durationPrebuffer = latency * 0.25;
|
||||
|
||||
inWrite = NO;
|
||||
inPeek = NO;
|
||||
inRead = NO;
|
||||
inMerge = NO;
|
||||
|
||||
[self setPreviousNode:p];
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
[self initLogFiles];
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// DSP threads buffer for low latency, and therefore should have high priority
|
||||
- (void)threadEntry:(id _Nullable)arg {
|
||||
@autoreleasepool {
|
||||
NSThread *currentThread = [NSThread currentThread];
|
||||
[currentThread setThreadPriority:0.75];
|
||||
[currentThread setQualityOfService:NSQualityOfServiceUserInitiated];
|
||||
threadTerminated = NO;
|
||||
[self process];
|
||||
threadTerminated = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
BOOL currentShouldContinue = shouldContinue;
|
||||
shouldContinue = s;
|
||||
if(!currentShouldContinue && s && threadTerminated) {
|
||||
[self launchThread];
|
||||
}
|
||||
}
|
||||
|
||||
- (double)secondsBuffered {
|
||||
return [buffer listDuration];
|
||||
}
|
||||
|
||||
@end
|
|
@ -12,9 +12,9 @@
|
|||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import "AudioDecoder.h"
|
||||
#import "Node.h"
|
||||
#import "Plugin.h"
|
||||
#import <CogAudio/AudioDecoder.h>
|
||||
#import <CogAudio/Node.h>
|
||||
#import <CogAudio/Plugin.h>
|
||||
|
||||
#define INPUT_NODE_SEEK
|
||||
|
||||
|
@ -33,20 +33,22 @@
|
|||
|
||||
Semaphore *exitAtTheEndOfTheStream;
|
||||
}
|
||||
@property(readonly) Semaphore *exitAtTheEndOfTheStream;
|
||||
@property(readonly) Semaphore * _Nonnull exitAtTheEndOfTheStream;
|
||||
@property(readonly) BOOL threadExited;
|
||||
|
||||
- (BOOL)openWithSource:(id<CogSource>)source;
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)d;
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p;
|
||||
|
||||
- (BOOL)openWithSource:(id<CogSource>_Nonnull)source;
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>_Nonnull)d;
|
||||
|
||||
- (void)process;
|
||||
- (NSDictionary *)properties;
|
||||
- (NSDictionary *_Nonnull)properties;
|
||||
- (void)seek:(long)frame;
|
||||
|
||||
- (void)registerObservers;
|
||||
|
||||
- (BOOL)setTrack:(NSURL *)track;
|
||||
- (BOOL)setTrack:(NSURL *_Nonnull)track;
|
||||
|
||||
- (id<CogDecoder>)decoder;
|
||||
- (id<CogDecoder>_Nonnull)decoder;
|
||||
|
||||
@end
|
||||
|
|
|
@ -159,14 +159,12 @@ static void *kInputNodeContext = &kInputNodeContext;
|
|||
|
||||
while([self shouldContinue] == YES && [self endOfStream] == NO) {
|
||||
if(shouldSeek == YES) {
|
||||
BufferChain *bufferChain = [[controller controller] bufferChain];
|
||||
ConverterNode *converter = [bufferChain converter];
|
||||
DLog(@"SEEKING! Resetting Buffer");
|
||||
BufferChain *bufferChain = controller;
|
||||
AudioPlayer *audioPlayer = [bufferChain controller];
|
||||
OutputNode *outputNode = [audioPlayer output];
|
||||
|
||||
// This resets the converter's buffer
|
||||
[self resetBuffer];
|
||||
[converter resetBuffer];
|
||||
[converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]];
|
||||
DLog(@"SEEKING! Resetting Buffer");
|
||||
[outputNode resetBackwards];
|
||||
|
||||
DLog(@"Reset buffer!");
|
||||
|
||||
|
@ -195,6 +193,11 @@ static void *kInputNodeContext = &kInputNodeContext;
|
|||
chunk = nil;
|
||||
}
|
||||
} else {
|
||||
if(chunk) {
|
||||
@autoreleasepool {
|
||||
chunk = nil;
|
||||
}
|
||||
}
|
||||
DLog(@"End of stream? %@", [self properties]);
|
||||
|
||||
endOfStream = YES;
|
||||
|
@ -234,7 +237,8 @@ static void *kInputNodeContext = &kInputNodeContext;
|
|||
seekFrame = frame;
|
||||
shouldSeek = YES;
|
||||
DLog(@"Should seek!");
|
||||
[semaphore signal];
|
||||
[self resetBuffer];
|
||||
[writeSemaphore signal];
|
||||
|
||||
if(endOfStream) {
|
||||
[exitAtTheEndOfTheStream signal];
|
||||
|
@ -268,6 +272,7 @@ static void *kInputNodeContext = &kInputNodeContext;
|
|||
- (void)dealloc {
|
||||
DLog(@"Input Node dealloc");
|
||||
[self removeObservers];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
- (NSDictionary *)properties {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// Copyright 2006 Vincent Spader. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ChunkList.h"
|
||||
#import <CogAudio/ChunkList.h>
|
||||
#import <CogAudio/CogSemaphore.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
@ -15,9 +15,12 @@
|
|||
#define BUFFER_SIZE 1024 * 1024
|
||||
#define CHUNK_SIZE 16 * 1024
|
||||
|
||||
//#define LOG_CHAINS 1
|
||||
|
||||
@interface Node : NSObject {
|
||||
ChunkList *buffer;
|
||||
Semaphore *semaphore;
|
||||
Semaphore *writeSemaphore;
|
||||
Semaphore *readSemaphore;
|
||||
|
||||
NSLock *accessLock;
|
||||
|
||||
|
@ -26,6 +29,11 @@
|
|||
|
||||
BOOL shouldReset;
|
||||
|
||||
BOOL inWrite;
|
||||
BOOL inPeek;
|
||||
BOOL inRead;
|
||||
BOOL inMerge;
|
||||
|
||||
BOOL shouldContinue;
|
||||
BOOL endOfStream; // All data is now in buffer
|
||||
BOOL initialBufferFilled;
|
||||
|
@ -33,15 +41,34 @@
|
|||
AudioStreamBasicDescription nodeFormat;
|
||||
uint32_t nodeChannelConfig;
|
||||
BOOL nodeLossless;
|
||||
|
||||
double durationPrebuffer;
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
NSFileHandle *logFileOut;
|
||||
NSFileHandle *logFileIn;
|
||||
#endif
|
||||
}
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p;
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
- (void)initLogFiles;
|
||||
#endif
|
||||
|
||||
- (void)cleanUp;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)writeData:(const void *_Nonnull)ptr amount:(size_t)a;
|
||||
- (void)writeChunk:(AudioChunk *_Nonnull)chunk;
|
||||
- (AudioChunk *_Nonnull)readChunk:(size_t)maxFrames;
|
||||
- (AudioChunk *_Nonnull)readChunkAsFloat32:(size_t)maxFrames;
|
||||
|
||||
- (AudioChunk *_Nonnull)readAndMergeChunks:(size_t)maxFrames;
|
||||
- (AudioChunk *_Nonnull)readAndMergeChunksAsFloat32:(size_t)maxFrames;
|
||||
|
||||
- (BOOL)peekFormat:(AudioStreamBasicDescription *_Nonnull)format channelConfig:(uint32_t *_Nonnull)config;
|
||||
- (BOOL)peekTimestamp:(double *_Nonnull)timestamp timeRatio:(double *_Nonnull)timeRatio;
|
||||
|
||||
- (void)process; // Should be overwriten by subclass
|
||||
- (void)threadEntry:(id _Nullable)arg;
|
||||
|
@ -50,6 +77,7 @@
|
|||
|
||||
- (void)setShouldReset:(BOOL)s;
|
||||
- (BOOL)shouldReset;
|
||||
- (void)resetBackwards;
|
||||
|
||||
- (void)setPreviousNode:(id _Nullable)p;
|
||||
- (id _Nullable)previousNode;
|
||||
|
@ -64,7 +92,8 @@
|
|||
- (uint32_t)nodeChannelConfig;
|
||||
- (BOOL)nodeLossless;
|
||||
|
||||
- (Semaphore *_Nonnull)semaphore;
|
||||
- (Semaphore *_Nonnull)writeSemaphore;
|
||||
- (Semaphore *_Nonnull)readSemaphore;
|
||||
|
||||
//-(void)resetBuffer;
|
||||
|
||||
|
|
|
@ -11,19 +11,45 @@
|
|||
#import "BufferChain.h"
|
||||
#import "Logging.h"
|
||||
|
||||
#import "OutputAVFoundation.h"
|
||||
#import "OutputCoreAudio.h"
|
||||
|
||||
#import <pthread.h>
|
||||
|
||||
#import <mach/mach_time.h>
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
#import "NSFileHandle+CreateFile.h"
|
||||
|
||||
static NSLock * _Node_lock = nil;
|
||||
static uint64_t _Node_serial;
|
||||
#endif
|
||||
|
||||
@implementation Node
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
+ (void)initialize {
|
||||
@synchronized (_Node_lock) {
|
||||
if(!_Node_lock) {
|
||||
_Node_lock = [[NSLock alloc] init];
|
||||
_Node_serial = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)initLogFiles {
|
||||
[_Node_lock lock];
|
||||
logFileOut = [NSFileHandle fileHandleForWritingAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_output_%08lld.raw", [self className], _Node_serial++]] createFile:YES];
|
||||
logFileIn = [NSFileHandle fileHandleForWritingAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_input_%08lld.raw", [self className], _Node_serial++]] createFile:YES];
|
||||
[_Node_lock unlock];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id)initWithController:(id)c previous:(id)p {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:3.0];
|
||||
semaphore = [[Semaphore alloc] init];
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:10.0];
|
||||
writeSemaphore = [[Semaphore alloc] init];
|
||||
readSemaphore = [[Semaphore alloc] init];
|
||||
|
||||
accessLock = [[NSLock alloc] init];
|
||||
|
||||
|
@ -36,12 +62,38 @@
|
|||
nodeChannelConfig = 0;
|
||||
nodeLossless = NO;
|
||||
|
||||
durationPrebuffer = 2.0;
|
||||
|
||||
inWrite = NO;
|
||||
inPeek = NO;
|
||||
inRead = NO;
|
||||
inMerge = NO;
|
||||
|
||||
[self setPreviousNode:p];
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
[self initLogFiles];
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self cleanUp];
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
[self setShouldContinue:NO];
|
||||
while(inWrite || inPeek || inRead || inMerge) {
|
||||
[writeSemaphore signal];
|
||||
if(previousNode) {
|
||||
[[previousNode readSemaphore] signal];
|
||||
}
|
||||
usleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioStreamBasicDescription)nodeFormat {
|
||||
return nodeFormat;
|
||||
}
|
||||
|
@ -55,6 +107,12 @@
|
|||
}
|
||||
|
||||
- (void)writeData:(const void *)ptr amount:(size_t)amount {
|
||||
inWrite = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inWrite = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
AudioChunk *chunk = [[AudioChunk alloc] init];
|
||||
|
@ -65,59 +123,104 @@
|
|||
[chunk setLossless:nodeLossless];
|
||||
[chunk assignSamples:ptr frameCount:amount / nodeFormat.mBytesPerPacket];
|
||||
|
||||
const double chunkDuration = [chunk duration];
|
||||
double durationLeft = [buffer maxDuration] - [buffer listDuration];
|
||||
#ifdef LOG_CHAINS
|
||||
if(logFileOut) {
|
||||
[logFileOut writeData:[NSData dataWithBytes:ptr length:amount]];
|
||||
}
|
||||
#endif
|
||||
|
||||
while(shouldContinue == YES && chunkDuration > durationLeft) {
|
||||
if(durationLeft < chunkDuration) {
|
||||
if(initialBufferFilled == NO) {
|
||||
initialBufferFilled = YES;
|
||||
if([controller respondsToSelector:@selector(initialBufferFilled:)])
|
||||
[controller performSelector:@selector(initialBufferFilled:) withObject:self];
|
||||
}
|
||||
double durationList = [buffer listDuration];
|
||||
double durationLeft = [buffer maxDuration] - durationList;
|
||||
|
||||
if(shouldContinue == YES && durationList >= durationPrebuffer) {
|
||||
if(initialBufferFilled == NO) {
|
||||
initialBufferFilled = YES;
|
||||
if([controller respondsToSelector:@selector(initialBufferFilled:)])
|
||||
[controller performSelector:@selector(initialBufferFilled:) withObject:self];
|
||||
}
|
||||
}
|
||||
|
||||
if(durationLeft < chunkDuration || shouldReset) {
|
||||
while(shouldContinue == YES && ![self paused] && durationLeft < 0.0) {
|
||||
if(durationLeft < 0.0 || shouldReset) {
|
||||
[accessLock unlock];
|
||||
[semaphore wait];
|
||||
[writeSemaphore timedWait:2000];
|
||||
[accessLock lock];
|
||||
}
|
||||
|
||||
durationLeft = [buffer maxDuration] - [buffer listDuration];
|
||||
}
|
||||
|
||||
[buffer addChunk:chunk];
|
||||
BOOL doSignal = NO;
|
||||
if([chunk frameCount]) {
|
||||
[buffer addChunk:chunk];
|
||||
doSignal = YES;
|
||||
}
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
if(doSignal) {
|
||||
[readSemaphore signal];
|
||||
}
|
||||
|
||||
inWrite = NO;
|
||||
}
|
||||
|
||||
- (void)writeChunk:(AudioChunk *)chunk {
|
||||
inWrite = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inWrite = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
const double chunkDuration = [chunk duration];
|
||||
double durationLeft = [buffer maxDuration] - [buffer listDuration];
|
||||
double durationList = [buffer listDuration];
|
||||
double durationLeft = [buffer maxDuration] - durationList;
|
||||
|
||||
while(shouldContinue == YES && chunkDuration > durationLeft) {
|
||||
if(durationLeft < chunkDuration) {
|
||||
if(initialBufferFilled == NO) {
|
||||
initialBufferFilled = YES;
|
||||
if([controller respondsToSelector:@selector(initialBufferFilled:)])
|
||||
[controller performSelector:@selector(initialBufferFilled:) withObject:self];
|
||||
}
|
||||
if(shouldContinue == YES && durationList >= durationPrebuffer) {
|
||||
if(initialBufferFilled == NO) {
|
||||
initialBufferFilled = YES;
|
||||
if([controller respondsToSelector:@selector(initialBufferFilled:)])
|
||||
[controller performSelector:@selector(initialBufferFilled:) withObject:self];
|
||||
}
|
||||
}
|
||||
|
||||
while(shouldContinue == YES && ![self paused] && durationLeft < 0.0) {
|
||||
if(previousNode && [previousNode shouldContinue] == NO) {
|
||||
shouldContinue = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
if(durationLeft < chunkDuration || shouldReset) {
|
||||
if(durationLeft < 0.0 || shouldReset) {
|
||||
[accessLock unlock];
|
||||
[semaphore wait];
|
||||
[writeSemaphore timedWait:2000];
|
||||
[accessLock lock];
|
||||
}
|
||||
|
||||
durationLeft = [buffer maxDuration] - [buffer listDuration];
|
||||
}
|
||||
|
||||
[buffer addChunk:chunk];
|
||||
BOOL doSignal = NO;
|
||||
if([chunk frameCount]) {
|
||||
#ifdef LOG_CHAINS
|
||||
if(logFileOut) {
|
||||
AudioChunk *chunkCopy = [chunk copy];
|
||||
size_t frameCount = [chunkCopy frameCount];
|
||||
NSData *chunkData = [chunkCopy removeSamples:frameCount];
|
||||
[logFileOut writeData:chunkData];
|
||||
}
|
||||
#endif
|
||||
[buffer addChunk:chunk];
|
||||
doSignal = YES;
|
||||
}
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
if(doSignal) {
|
||||
[readSemaphore signal];
|
||||
}
|
||||
|
||||
inWrite = NO;
|
||||
}
|
||||
|
||||
// Should be overwriten by subclass.
|
||||
|
@ -131,21 +234,110 @@
|
|||
}
|
||||
|
||||
- (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config {
|
||||
inPeek = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inPeek = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
while(shouldContinue && ![self paused] &&
|
||||
[[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) {
|
||||
[accessLock unlock];
|
||||
[writeSemaphore signal];
|
||||
[[previousNode readSemaphore] timedWait:2000];
|
||||
[accessLock lock];
|
||||
}
|
||||
|
||||
if(!shouldContinue || [self paused]) {
|
||||
[accessLock unlock];
|
||||
inPeek = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
[accessLock unlock];
|
||||
inPeek = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL ret = [[previousNode buffer] peekFormat:format channelConfig:config];
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
inPeek = NO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (BOOL)peekTimestamp:(double *_Nonnull)timestamp timeRatio:(double *_Nonnull)timeRatio {
|
||||
inPeek = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inPeek = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
while(shouldContinue && ![self paused] &&
|
||||
[[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) {
|
||||
[accessLock unlock];
|
||||
[writeSemaphore signal];
|
||||
[[previousNode readSemaphore] timedWait:2000];
|
||||
[accessLock lock];
|
||||
}
|
||||
|
||||
if(!shouldContinue || [self paused]) {
|
||||
[accessLock unlock];
|
||||
inPeek = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
[accessLock unlock];
|
||||
inPeek = NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL ret = [[previousNode buffer] peekTimestamp:timestamp timeRatio:timeRatio];
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
inPeek = NO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (AudioChunk *)readChunk:(size_t)maxFrames {
|
||||
inRead = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inRead = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
endOfStream = YES;
|
||||
while(shouldContinue && ![self paused] &&
|
||||
[[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) {
|
||||
[accessLock unlock];
|
||||
[writeSemaphore signal];
|
||||
[[previousNode readSemaphore] timedWait:2000];
|
||||
[accessLock lock];
|
||||
if([previousNode shouldReset] == YES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!shouldContinue || [self paused]) {
|
||||
[accessLock unlock];
|
||||
inRead = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
[accessLock unlock];
|
||||
inRead = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
|
@ -157,7 +349,7 @@
|
|||
shouldReset = YES;
|
||||
[previousNode setShouldReset:NO];
|
||||
|
||||
[[previousNode semaphore] signal];
|
||||
[[previousNode writeSemaphore] signal];
|
||||
}
|
||||
|
||||
AudioChunk *ret;
|
||||
|
@ -169,18 +361,52 @@
|
|||
[accessLock unlock];
|
||||
|
||||
if([ret frameCount]) {
|
||||
[[previousNode semaphore] signal];
|
||||
[[previousNode writeSemaphore] signal];
|
||||
}
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
if(logFileIn) {
|
||||
AudioChunk *chunkCopy = [ret copy];
|
||||
size_t frameCount = [chunkCopy frameCount];
|
||||
NSData *chunkData = [chunkCopy removeSamples:frameCount];
|
||||
[logFileIn writeData:chunkData];
|
||||
}
|
||||
#endif
|
||||
|
||||
inRead = NO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (AudioChunk *)readChunkAsFloat32:(size_t)maxFrames {
|
||||
inRead = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inRead = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
endOfStream = YES;
|
||||
while(shouldContinue && ![self paused] &&
|
||||
[[previousNode buffer] isEmpty] && [previousNode endOfStream] == NO) {
|
||||
[accessLock unlock];
|
||||
[writeSemaphore signal];
|
||||
[[previousNode readSemaphore] timedWait:2000];
|
||||
[accessLock lock];
|
||||
if([previousNode shouldReset] == YES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!shouldContinue || [self paused]) {
|
||||
[accessLock unlock];
|
||||
inRead = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
[accessLock unlock];
|
||||
inRead = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
|
@ -192,7 +418,7 @@
|
|||
shouldReset = YES;
|
||||
[previousNode setShouldReset:NO];
|
||||
|
||||
[[previousNode semaphore] signal];
|
||||
[[previousNode writeSemaphore] signal];
|
||||
}
|
||||
|
||||
AudioChunk *ret;
|
||||
|
@ -204,9 +430,134 @@
|
|||
[accessLock unlock];
|
||||
|
||||
if([ret frameCount]) {
|
||||
[[previousNode semaphore] signal];
|
||||
[[previousNode writeSemaphore] signal];
|
||||
}
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
if(logFileIn) {
|
||||
AudioChunk *chunkCopy = [ret copy];
|
||||
size_t frameCount = [chunkCopy frameCount];
|
||||
NSData *chunkData = [chunkCopy removeSamples:frameCount];
|
||||
[logFileIn writeData:chunkData];
|
||||
}
|
||||
#endif
|
||||
|
||||
inRead = NO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (AudioChunk *)readAndMergeChunks:(size_t)maxFrames {
|
||||
inMerge = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inMerge = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
[accessLock unlock];
|
||||
inMerge = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
AudioChunk *ret;
|
||||
|
||||
@autoreleasepool {
|
||||
ret = [[previousNode buffer] removeAndMergeSamples:maxFrames callBlock:^BOOL{
|
||||
if([previousNode shouldReset] == YES) {
|
||||
@autoreleasepool {
|
||||
[buffer reset];
|
||||
}
|
||||
|
||||
shouldReset = YES;
|
||||
[previousNode setShouldReset:NO];
|
||||
}
|
||||
|
||||
[accessLock unlock];
|
||||
[[previousNode writeSemaphore] signal];
|
||||
[[previousNode readSemaphore] timedWait:2000];
|
||||
[accessLock lock];
|
||||
|
||||
return !shouldContinue || [self paused] || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES);
|
||||
}];
|
||||
}
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
if([ret frameCount]) {
|
||||
[[previousNode writeSemaphore] signal];
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
if(logFileIn) {
|
||||
AudioChunk *chunkCopy = [ret copy];
|
||||
size_t frameCount = [chunkCopy frameCount];
|
||||
NSData *chunkData = [chunkCopy removeSamples:frameCount];
|
||||
[logFileIn writeData:chunkData];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inMerge = NO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (AudioChunk *)readAndMergeChunksAsFloat32:(size_t)maxFrames {
|
||||
inMerge = YES;
|
||||
if(!shouldContinue || [self paused]) {
|
||||
inMerge = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
[accessLock lock];
|
||||
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
[accessLock unlock];
|
||||
inMerge = NO;
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
AudioChunk *ret;
|
||||
|
||||
@autoreleasepool {
|
||||
ret = [[previousNode buffer] removeAndMergeSamplesAsFloat32:maxFrames callBlock:^BOOL{
|
||||
if([previousNode shouldReset] == YES) {
|
||||
@autoreleasepool {
|
||||
[buffer reset];
|
||||
}
|
||||
|
||||
shouldReset = YES;
|
||||
[previousNode setShouldReset:NO];
|
||||
}
|
||||
|
||||
[accessLock unlock];
|
||||
[[previousNode writeSemaphore] signal];
|
||||
[[previousNode readSemaphore] timedWait:2000];
|
||||
[accessLock lock];
|
||||
|
||||
return !shouldContinue || [self paused] || ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES);
|
||||
}];
|
||||
}
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
if([ret frameCount]) {
|
||||
[[previousNode writeSemaphore] signal];
|
||||
|
||||
#ifdef LOG_CHAINS
|
||||
if(logFileIn) {
|
||||
AudioChunk *chunkCopy = [ret copy];
|
||||
size_t frameCount = [chunkCopy frameCount];
|
||||
NSData *chunkData = [chunkCopy removeSamples:frameCount];
|
||||
[logFileIn writeData:chunkData];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inMerge = NO;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -245,8 +596,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (Semaphore *)semaphore {
|
||||
return semaphore;
|
||||
- (void)lockedResetBuffer {
|
||||
@autoreleasepool {
|
||||
[buffer reset];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)unlockedResetBuffer {
|
||||
@autoreleasepool {
|
||||
[accessLock lock];
|
||||
[buffer reset];
|
||||
[accessLock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations should override
|
||||
- (BOOL)paused {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (Semaphore *)writeSemaphore {
|
||||
return writeSemaphore;
|
||||
}
|
||||
|
||||
- (Semaphore *)readSemaphore {
|
||||
return readSemaphore;
|
||||
}
|
||||
|
||||
- (BOOL)endOfStream {
|
||||
|
@ -269,4 +643,23 @@
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
// Reset everything in the chain
|
||||
- (void)resetBackwards {
|
||||
[accessLock lock];
|
||||
if(buffer) {
|
||||
[self lockedResetBuffer];
|
||||
[writeSemaphore signal];
|
||||
[readSemaphore signal];
|
||||
}
|
||||
Node *node = previousNode;
|
||||
while(node) {
|
||||
[node unlockedResetBuffer];
|
||||
[node setShouldReset:YES];
|
||||
[[node writeSemaphore] signal];
|
||||
[[node readSemaphore] signal];
|
||||
node = [node previousNode];
|
||||
}
|
||||
[accessLock unlock];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import "Node.h"
|
||||
#import "OutputAVFoundation.h"
|
||||
#import <CogAudio/Node.h>
|
||||
#import <CogAudio/OutputCoreAudio.h>
|
||||
|
||||
@interface OutputNode : Node {
|
||||
AudioStreamBasicDescription format;
|
||||
|
@ -21,21 +21,18 @@
|
|||
|
||||
double amountPlayed;
|
||||
double amountPlayedInterval;
|
||||
OutputAVFoundation *output;
|
||||
OutputCoreAudio *output;
|
||||
|
||||
BOOL paused;
|
||||
BOOL started;
|
||||
BOOL intervalReported;
|
||||
}
|
||||
|
||||
- (void)beginEqualizer:(AudioUnit)eq;
|
||||
- (void)refreshEqualizer:(AudioUnit)eq;
|
||||
- (void)endEqualizer:(AudioUnit)eq;
|
||||
|
||||
- (double)amountPlayed;
|
||||
- (double)amountPlayedInterval;
|
||||
|
||||
- (void)incrementAmountPlayed:(double)seconds;
|
||||
- (void)setAmountPlayed:(double)seconds;
|
||||
- (void)resetAmountPlayed;
|
||||
- (void)resetAmountPlayedInterval;
|
||||
|
||||
|
@ -46,12 +43,15 @@
|
|||
|
||||
- (double)secondsBuffered;
|
||||
|
||||
- (void)setup;
|
||||
- (BOOL)setup;
|
||||
- (BOOL)setupWithInterval:(BOOL)resumeInterval;
|
||||
- (void)process;
|
||||
- (void)close;
|
||||
- (void)seek:(double)time;
|
||||
|
||||
- (double)latency;
|
||||
- (void)fadeOut;
|
||||
- (void)fadeOutBackground;
|
||||
- (void)fadeIn;
|
||||
|
||||
- (AudioChunk *)readChunk:(size_t)amount;
|
||||
|
||||
|
@ -59,6 +59,10 @@
|
|||
- (AudioStreamBasicDescription)format;
|
||||
- (uint32_t)config;
|
||||
|
||||
- (AudioStreamBasicDescription)deviceFormat;
|
||||
- (uint32_t)deviceChannelConfig;
|
||||
|
||||
- (double)volume;
|
||||
- (void)setVolume:(double)v;
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s;
|
||||
|
@ -74,4 +78,12 @@
|
|||
|
||||
- (void)restartPlaybackAtCurrentPosition;
|
||||
|
||||
- (double)latency;
|
||||
- (double)getVisLatency;
|
||||
- (double)getTotalLatency;
|
||||
|
||||
- (id)controller;
|
||||
|
||||
- (id)downmix;
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,23 +9,69 @@
|
|||
#import "OutputNode.h"
|
||||
#import "AudioPlayer.h"
|
||||
#import "BufferChain.h"
|
||||
#import "OutputAVFoundation.h"
|
||||
#import "OutputCoreAudio.h"
|
||||
|
||||
#import "DSPRubberbandNode.h"
|
||||
#import "DSPFSurroundNode.h"
|
||||
#import "DSPEqualizerNode.h"
|
||||
#import "VisualizationNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation OutputNode
|
||||
@implementation OutputNode {
|
||||
BOOL DSPsLaunched;
|
||||
|
||||
- (void)setup {
|
||||
amountPlayed = 0.0;
|
||||
amountPlayedInterval = 0.0;
|
||||
Node *previousInput;
|
||||
|
||||
DSPRubberbandNode *rubberbandNode;
|
||||
DSPFSurroundNode *fsurroundNode;
|
||||
DSPEqualizerNode *equalizerNode;
|
||||
VisualizationNode *visualizationNode;
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
return [self setupWithInterval:NO];
|
||||
}
|
||||
|
||||
- (BOOL)setupWithInterval:(BOOL)resumeInterval {
|
||||
if(!resumeInterval) {
|
||||
amountPlayed = 0.0;
|
||||
amountPlayedInterval = 0.0;
|
||||
intervalReported = NO;
|
||||
}
|
||||
|
||||
paused = YES;
|
||||
started = NO;
|
||||
intervalReported = NO;
|
||||
|
||||
output = [[OutputAVFoundation alloc] initWithController:self];
|
||||
output = [[OutputCoreAudio alloc] initWithController:self];
|
||||
|
||||
[output setup];
|
||||
if(![output setup]) {
|
||||
output = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
if(!DSPsLaunched) {
|
||||
rubberbandNode = [[DSPRubberbandNode alloc] initWithController:self previous:nil latency:0.1];
|
||||
if(!rubberbandNode) return NO;
|
||||
fsurroundNode = [[DSPFSurroundNode alloc] initWithController:self previous:rubberbandNode latency:0.03];
|
||||
if(!fsurroundNode) return NO;
|
||||
equalizerNode = [[DSPEqualizerNode alloc] initWithController:self previous:fsurroundNode latency:0.03];
|
||||
if(!equalizerNode) return NO;
|
||||
|
||||
// Approximately double the chunk size for Vis at 44100Hz
|
||||
visualizationNode = [[VisualizationNode alloc] initWithController:self previous:equalizerNode latency:8192.0 / 44100.0];
|
||||
if(!visualizationNode) return NO;
|
||||
|
||||
[self setPreviousNode:visualizationNode];
|
||||
|
||||
DSPsLaunched = YES;
|
||||
|
||||
[self launchDSPs];
|
||||
|
||||
previousInput = nil;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)seek:(double)time {
|
||||
|
@ -50,6 +96,19 @@
|
|||
[output resume];
|
||||
}
|
||||
|
||||
- (void)fadeOut {
|
||||
[output fadeOut];
|
||||
}
|
||||
|
||||
- (void)fadeOutBackground {
|
||||
[output fadeOutBackground];
|
||||
}
|
||||
|
||||
- (void)fadeIn {
|
||||
[self reconnectInputAndReplumb];
|
||||
[output fadeIn];
|
||||
}
|
||||
|
||||
- (void)incrementAmountPlayed:(double)seconds {
|
||||
amountPlayed += seconds;
|
||||
amountPlayedInterval += seconds;
|
||||
|
@ -59,6 +118,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setAmountPlayed:(double)seconds {
|
||||
double delta = seconds - amountPlayed;
|
||||
if(delta > 0.0 && delta < 5.0) {
|
||||
[self incrementAmountPlayed:delta];
|
||||
} else if(delta) {
|
||||
amountPlayed = seconds;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetAmountPlayed {
|
||||
amountPlayed = 0;
|
||||
}
|
||||
|
@ -69,7 +137,11 @@
|
|||
}
|
||||
|
||||
- (BOOL)selectNextBuffer {
|
||||
return [controller selectNextBuffer];
|
||||
BOOL ret = [controller selectNextBuffer];
|
||||
if(!ret) {
|
||||
[self reconnectInputAndReplumb];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)endOfInputPlayed {
|
||||
|
@ -89,25 +161,78 @@
|
|||
return [buffer listDuration];
|
||||
}
|
||||
|
||||
- (NSArray *)DSPs {
|
||||
if(DSPsLaunched) {
|
||||
return @[rubberbandNode, fsurroundNode, equalizerNode, visualizationNode];
|
||||
} else {
|
||||
return @[];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)reconnectInput {
|
||||
Node *finalNode = nil;
|
||||
if(rubberbandNode) {
|
||||
finalNode = [[controller bufferChain] finalNode];
|
||||
if(finalNode) {
|
||||
[rubberbandNode setPreviousNode:finalNode];
|
||||
}
|
||||
}
|
||||
|
||||
return !!finalNode;
|
||||
}
|
||||
|
||||
- (void)reconnectInputAndReplumb {
|
||||
Node *finalNode = nil;
|
||||
if(DSPsLaunched) {
|
||||
finalNode = [[controller bufferChain] finalNode];
|
||||
if(finalNode) {
|
||||
[rubberbandNode setPreviousNode:finalNode];
|
||||
}
|
||||
}
|
||||
|
||||
NSArray *DSPs = [self DSPs];
|
||||
|
||||
for (Node *node in DSPs) {
|
||||
[node setEndOfStream:NO];
|
||||
[node setShouldContinue:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)launchDSPs {
|
||||
NSArray *DSPs = [self DSPs];
|
||||
|
||||
for (Node *node in DSPs) {
|
||||
[node launchThread];
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)readChunk:(size_t)amount {
|
||||
@autoreleasepool {
|
||||
[self setPreviousNode:[[controller bufferChain] finalNode]];
|
||||
if([self reconnectInput]) {
|
||||
AudioChunk *ret = [super readChunk:amount];
|
||||
|
||||
AudioChunk *ret = [super readChunk:amount];
|
||||
if((!ret || ![ret frameCount]) && [previousNode endOfStream]) {
|
||||
endOfStream = YES;
|
||||
}
|
||||
|
||||
/* if (n == 0) {
|
||||
DLog(@"Output Buffer dry!");
|
||||
}
|
||||
*/
|
||||
return ret;
|
||||
return ret;
|
||||
} else {
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)peekFormat:(nonnull AudioStreamBasicDescription *)format channelConfig:(nonnull uint32_t *)config {
|
||||
@autoreleasepool {
|
||||
[self setPreviousNode:[[controller bufferChain] finalNode]];
|
||||
|
||||
return [super peekFormat:format channelConfig:config];
|
||||
if([self reconnectInput]) {
|
||||
BOOL ret = [super peekFormat:format channelConfig:config];
|
||||
if(!ret && [previousNode endOfStream]) {
|
||||
endOfStream = YES;
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,29 +252,63 @@
|
|||
return config;
|
||||
}
|
||||
|
||||
- (AudioStreamBasicDescription)deviceFormat {
|
||||
return [output deviceFormat];
|
||||
}
|
||||
|
||||
- (uint32_t)deviceChannelConfig {
|
||||
return [output deviceChannelConfig];
|
||||
}
|
||||
|
||||
- (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig {
|
||||
if(!shouldContinue) return;
|
||||
|
||||
format = *f;
|
||||
config = channelConfig;
|
||||
// Calculate a ratio and add to double(seconds) instead, as format may change
|
||||
// double oldSampleRatio = sampleRatio;
|
||||
BufferChain *bufferChain = [controller bufferChain];
|
||||
AudioPlayer *audioPlayer = controller;
|
||||
BufferChain *bufferChain = [audioPlayer bufferChain];
|
||||
if(bufferChain) {
|
||||
ConverterNode *converter = [bufferChain converter];
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
uint32_t outputChannelConfig;
|
||||
BOOL formatChanged = NO;
|
||||
if(converter) {
|
||||
// This clears the resampler buffer, but not the input buffer
|
||||
// We also have to jump the play position ahead accounting for
|
||||
// the data we are flushing
|
||||
amountPlayed += [[converter buffer] listDuration];
|
||||
|
||||
AudioStreamBasicDescription inf = [bufferChain inputFormat];
|
||||
uint32_t config = [bufferChain inputConfig];
|
||||
|
||||
format.mChannelsPerFrame = inf.mChannelsPerFrame;
|
||||
format.mBytesPerFrame = ((inf.mBitsPerChannel + 7) / 8) * format.mChannelsPerFrame;
|
||||
format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
|
||||
channelConfig = config;
|
||||
|
||||
[converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]];
|
||||
AudioStreamBasicDescription converterFormat = [converter nodeFormat];
|
||||
if(memcmp(&converterFormat, &format, sizeof(converterFormat)) != 0) {
|
||||
formatChanged = YES;
|
||||
}
|
||||
}
|
||||
DSPDownmixNode *downmixNode = nil;
|
||||
if(output) {
|
||||
downmixNode = [output downmix];
|
||||
}
|
||||
if(downmixNode && !formatChanged) {
|
||||
outputFormat = [output deviceFormat];
|
||||
outputChannelConfig = [output deviceChannelConfig];
|
||||
AudioStreamBasicDescription currentOutputFormat = [downmixNode nodeFormat];
|
||||
uint32_t currentOutputChannelConfig = [downmixNode nodeChannelConfig];
|
||||
if(memcmp(¤tOutputFormat, &outputFormat, sizeof(currentOutputFormat)) != 0 ||
|
||||
currentOutputChannelConfig != outputChannelConfig) {
|
||||
formatChanged = YES;
|
||||
}
|
||||
}
|
||||
if(formatChanged) {
|
||||
InputNode *inputNode = [bufferChain inputNode];
|
||||
if(converter) {
|
||||
[converter setOutputFormat:format];
|
||||
}
|
||||
if(downmixNode) {
|
||||
[downmixNode setOutputFormat:[output deviceFormat] withChannelConfig:[output deviceChannelConfig]];
|
||||
}
|
||||
if(inputNode) {
|
||||
AudioStreamBasicDescription inputFormat = [inputNode nodeFormat];
|
||||
if(converter) {
|
||||
[converter inputFormatDidChange:inputFormat inputConfig:[inputNode nodeChannelConfig]];
|
||||
}
|
||||
[inputNode seek:(long)(amountPlayed * inputFormat.mSampleRate)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +316,22 @@
|
|||
- (void)close {
|
||||
[output stop];
|
||||
output = nil;
|
||||
if(DSPsLaunched) {
|
||||
NSArray *DSPs = [self DSPs];
|
||||
for(Node *node in DSPs) {
|
||||
[node setShouldContinue:NO];
|
||||
}
|
||||
previousNode = nil;
|
||||
visualizationNode = nil;
|
||||
fsurroundNode = nil;
|
||||
rubberbandNode = nil;
|
||||
previousInput = nil;
|
||||
DSPsLaunched = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (double)volume {
|
||||
return [output volume];
|
||||
}
|
||||
|
||||
- (void)setVolume:(double)v {
|
||||
|
@ -166,6 +341,10 @@
|
|||
- (void)setShouldContinue:(BOOL)s {
|
||||
[super setShouldContinue:s];
|
||||
|
||||
NSArray *DSPs = [self DSPs];
|
||||
for(Node *node in DSPs) {
|
||||
[node setShouldContinue:s];
|
||||
}
|
||||
// if (s == NO)
|
||||
// [output stop];
|
||||
}
|
||||
|
@ -178,18 +357,6 @@
|
|||
return paused;
|
||||
}
|
||||
|
||||
- (void)beginEqualizer:(AudioUnit)eq {
|
||||
[controller beginEqualizer:eq];
|
||||
}
|
||||
|
||||
- (void)refreshEqualizer:(AudioUnit)eq {
|
||||
[controller refreshEqualizer:eq];
|
||||
}
|
||||
|
||||
- (void)endEqualizer:(AudioUnit)eq {
|
||||
[controller endEqualizer:eq];
|
||||
}
|
||||
|
||||
- (void)sustainHDCD {
|
||||
[output sustainHDCD];
|
||||
}
|
||||
|
@ -199,7 +366,28 @@
|
|||
}
|
||||
|
||||
- (double)latency {
|
||||
return [output latency];
|
||||
double latency = 0.0;
|
||||
NSArray *DSPs = [self DSPs];
|
||||
for(Node *node in DSPs) {
|
||||
latency += [node secondsBuffered];
|
||||
}
|
||||
return [output latency] + latency;
|
||||
}
|
||||
|
||||
- (double)getVisLatency {
|
||||
return [output latency] + [visualizationNode secondsBuffered];
|
||||
}
|
||||
|
||||
- (double)getTotalLatency {
|
||||
return [[controller bufferChain] secondsBuffered] + [self latency];
|
||||
}
|
||||
|
||||
- (id)controller {
|
||||
return controller;
|
||||
}
|
||||
|
||||
- (id)downmix {
|
||||
return [output downmix];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
35
Audio/Chain/VisualizationNode.h
Normal file
35
Audio/Chain/VisualizationNode.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// VisualizationNode.h
|
||||
// CogAudio
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/12/25.
|
||||
//
|
||||
|
||||
#ifndef VisualizationNode_h
|
||||
#define VisualizationNode_h
|
||||
|
||||
#import <CogAudio/Node.h>
|
||||
|
||||
@interface VisualizationNode : Node {
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency;
|
||||
|
||||
- (void)threadEntry:(id _Nullable)arg;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (void)cleanUp;
|
||||
|
||||
- (BOOL)paused;
|
||||
|
||||
- (void)resetBuffer;
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s;
|
||||
|
||||
- (void)process;
|
||||
|
||||
- (double)secondsBuffered;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* VisualizationNode_h */
|
273
Audio/Chain/VisualizationNode.m
Normal file
273
Audio/Chain/VisualizationNode.m
Normal file
|
@ -0,0 +1,273 @@
|
|||
//
|
||||
// VisualizationNode.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/12/25.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
#import "Downmix.h"
|
||||
|
||||
#import <CogAudio/VisualizationController.h>
|
||||
|
||||
#import "BufferChain.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
#import "rsstate.h"
|
||||
|
||||
#import "VisualizationNode.h"
|
||||
|
||||
@implementation VisualizationNode {
|
||||
void *rs;
|
||||
double lastVisRate;
|
||||
|
||||
BOOL processEntered;
|
||||
BOOL stopping;
|
||||
BOOL paused;
|
||||
BOOL threadTerminated;
|
||||
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
AudioStreamBasicDescription visFormat; // Mono format for vis
|
||||
|
||||
uint32_t inputChannelConfig;
|
||||
uint32_t visChannelConfig;
|
||||
|
||||
size_t resamplerRemain;
|
||||
|
||||
DownmixProcessor *downmixer;
|
||||
|
||||
VisualizationController *visController;
|
||||
|
||||
float visAudio[512];
|
||||
float resamplerInput[8192];
|
||||
float visTemp[8192];
|
||||
}
|
||||
|
||||
- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:latency];
|
||||
|
||||
writeSemaphore = [[Semaphore alloc] init];
|
||||
readSemaphore = [[Semaphore alloc] init];
|
||||
|
||||
accessLock = [[NSLock alloc] init];
|
||||
|
||||
initialBufferFilled = NO;
|
||||
|
||||
controller = c;
|
||||
endOfStream = NO;
|
||||
shouldContinue = YES;
|
||||
|
||||
nodeChannelConfig = 0;
|
||||
nodeLossless = NO;
|
||||
|
||||
durationPrebuffer = latency * 0.25;
|
||||
|
||||
visController = [VisualizationController sharedController];
|
||||
|
||||
inWrite = NO;
|
||||
inPeek = NO;
|
||||
inRead = NO;
|
||||
inMerge = NO;
|
||||
|
||||
[self setPreviousNode:p];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
DLog(@"Visualization node dealloc");
|
||||
[self setShouldContinue:NO];
|
||||
[self cleanUp];
|
||||
[super cleanUp];
|
||||
}
|
||||
|
||||
// Visualization thread should be fairly high priority, too
|
||||
- (void)threadEntry:(id _Nullable)arg {
|
||||
@autoreleasepool {
|
||||
NSThread *currentThread = [NSThread currentThread];
|
||||
[currentThread setThreadPriority:0.75];
|
||||
[currentThread setQualityOfService:NSQualityOfServiceUserInitiated];
|
||||
threadTerminated = NO;
|
||||
[self process];
|
||||
threadTerminated = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resetBuffer {
|
||||
paused = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[buffer reset];
|
||||
[self fullShutdown];
|
||||
paused = NO;
|
||||
}
|
||||
|
||||
- (double)secondsBuffered {
|
||||
return [buffer listDuration];
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
BOOL currentShouldContinue = shouldContinue;
|
||||
shouldContinue = s;
|
||||
if(!currentShouldContinue && s && threadTerminated) {
|
||||
[self launchThread];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setup {
|
||||
if(fabs(inputFormat.mSampleRate - 44100.0) > 1e-6) {
|
||||
rs = rsstate_new(1, inputFormat.mSampleRate, 44100.0);
|
||||
if(!rs) {
|
||||
return NO;
|
||||
}
|
||||
resamplerRemain = 0;
|
||||
}
|
||||
|
||||
visFormat = inputFormat;
|
||||
visFormat.mChannelsPerFrame = 1;
|
||||
visFormat.mBytesPerFrame = sizeof(float);
|
||||
visFormat.mBytesPerPacket = visFormat.mBytesPerFrame * visFormat.mFramesPerPacket;
|
||||
visChannelConfig = AudioChannelFrontCenter;
|
||||
|
||||
downmixer = [[DownmixProcessor alloc] initWithInputFormat:inputFormat inputConfig:inputChannelConfig andOutputFormat:visFormat outputConfig:visChannelConfig];
|
||||
if(!downmixer) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)cleanUp {
|
||||
stopping = YES;
|
||||
while(processEntered) {
|
||||
usleep(500);
|
||||
}
|
||||
[self fullShutdown];
|
||||
}
|
||||
|
||||
- (void)fullShutdown {
|
||||
if(rs) {
|
||||
rsstate_delete(rs);
|
||||
rs = NULL;
|
||||
}
|
||||
downmixer = nil;
|
||||
}
|
||||
|
||||
- (BOOL)paused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)process {
|
||||
while([self shouldContinue] == YES) {
|
||||
if(paused || endOfStream) {
|
||||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
AudioChunk *chunk = nil;
|
||||
chunk = [self readAndMergeChunksAsFloat32:512];
|
||||
if(!chunk || ![chunk frameCount]) {
|
||||
if([previousNode endOfStream] == YES) {
|
||||
usleep(500);
|
||||
endOfStream = YES;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
[self processVis:[chunk copy]];
|
||||
[self writeChunk:chunk];
|
||||
chunk = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
endOfStream = YES;
|
||||
}
|
||||
|
||||
- (void)postVisPCM:(const float *)visTemp amount:(size_t)samples {
|
||||
[visController postVisPCM:visTemp amount:(int)samples];
|
||||
}
|
||||
|
||||
- (void)processVis:(AudioChunk *)chunk {
|
||||
processEntered = YES;
|
||||
|
||||
if(paused) {
|
||||
processEntered = NO;
|
||||
return;
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription format = [chunk format];
|
||||
uint32_t channelConfig = [chunk channelConfig];
|
||||
|
||||
[visController postSampleRate:44100.0];
|
||||
|
||||
if(!rs || !downmixer ||
|
||||
memcmp(&format, &inputFormat, sizeof(format)) != 0 ||
|
||||
channelConfig != inputChannelConfig) {
|
||||
if(rs) {
|
||||
while(!stopping) {
|
||||
int samplesFlushed;
|
||||
samplesFlushed = (int)rsstate_flush(rs, &visTemp[0], 8192);
|
||||
if(samplesFlushed > 1) {
|
||||
[self postVisPCM:visTemp amount:samplesFlushed];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
[self fullShutdown];
|
||||
inputFormat = format;
|
||||
inputChannelConfig = channelConfig;
|
||||
if(![self setup]) {
|
||||
processEntered = NO;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
NSData *sampleData = [chunk removeSamples:frameCount];
|
||||
|
||||
[downmixer process:[sampleData bytes] frameCount:frameCount output:&visAudio[0]];
|
||||
|
||||
if(rs) {
|
||||
int samplesProcessed;
|
||||
size_t totalDone = 0;
|
||||
size_t inDone = 0;
|
||||
size_t visFrameCount = frameCount;
|
||||
do {
|
||||
if(stopping) {
|
||||
break;
|
||||
}
|
||||
int visTodo = (int)MIN(visFrameCount, resamplerRemain + visFrameCount - 8192);
|
||||
if(visTodo) {
|
||||
cblas_scopy(visTodo, &visAudio[0], 1, &resamplerInput[resamplerRemain], 1);
|
||||
}
|
||||
visTodo += resamplerRemain;
|
||||
resamplerRemain = 0;
|
||||
samplesProcessed = (int)rsstate_resample(rs, &resamplerInput[0], visTodo, &inDone, &visTemp[0], 8192);
|
||||
resamplerRemain = (int)(visTodo - inDone);
|
||||
if(resamplerRemain && inDone) {
|
||||
memmove(&resamplerInput[0], &resamplerInput[inDone], resamplerRemain * sizeof(float));
|
||||
}
|
||||
if(samplesProcessed) {
|
||||
[self postVisPCM:&visTemp[0] amount:samplesProcessed];
|
||||
}
|
||||
totalDone += inDone;
|
||||
visFrameCount -= inDone;
|
||||
} while(samplesProcessed && visFrameCount);
|
||||
} else {
|
||||
[self postVisPCM:&visAudio[0] amount:frameCount];
|
||||
}
|
||||
|
||||
processEntered = NO;
|
||||
}
|
||||
|
||||
@end
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -25,8 +25,6 @@
|
|||
17D21CA80B8BE4BA00D1EBDE /* Node.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D21C7D0B8BE4BA00D1EBDE /* Node.m */; };
|
||||
17D21CA90B8BE4BA00D1EBDE /* OutputNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 17D21C7E0B8BE4BA00D1EBDE /* OutputNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
17D21CAA0B8BE4BA00D1EBDE /* OutputNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D21C7F0B8BE4BA00D1EBDE /* OutputNode.m */; };
|
||||
17D21CC50B8BE4BA00D1EBDE /* OutputAVFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 17D21C9C0B8BE4BA00D1EBDE /* OutputAVFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
17D21CC60B8BE4BA00D1EBDE /* OutputAVFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D21C9D0B8BE4BA00D1EBDE /* OutputAVFoundation.m */; };
|
||||
17D21CC70B8BE4BA00D1EBDE /* Status.h in Headers */ = {isa = PBXBuildFile; fileRef = 17D21C9E0B8BE4BA00D1EBDE /* Status.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
17D21CF30B8BE5EF00D1EBDE /* CogSemaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = 17D21CF10B8BE5EF00D1EBDE /* CogSemaphore.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
17D21CF40B8BE5EF00D1EBDE /* CogSemaphore.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D21CF20B8BE5EF00D1EBDE /* CogSemaphore.m */; };
|
||||
|
@ -49,50 +47,74 @@
|
|||
8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */ = {isa = PBXBuildFile; fileRef = 8328995527CB51B700D7F028 /* SHA256Digest.h */; };
|
||||
8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8328995627CB51B700D7F028 /* SHA256Digest.m */; };
|
||||
8328995A27CB51C900D7F028 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8328995927CB51C900D7F028 /* Security.framework */; };
|
||||
833442422D6EFA6700C51D38 /* VisualizationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 833442402D6EFA6700C51D38 /* VisualizationController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
833442432D6EFA6700C51D38 /* VisualizationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 833442412D6EFA6700C51D38 /* VisualizationController.m */; };
|
||||
833738EA2D5EA52500278628 /* DSPDownmixNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 833738E92D5EA52500278628 /* DSPDownmixNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
833738EC2D5EA53500278628 /* DSPDownmixNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 833738EB2D5EA53500278628 /* DSPDownmixNode.m */; };
|
||||
833738EF2D5EA5B700278628 /* Downmix.m in Sources */ = {isa = PBXBuildFile; fileRef = 833738EE2D5EA5B700278628 /* Downmix.m */; };
|
||||
833738F02D5EA5B700278628 /* Downmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 833738ED2D5EA5B700278628 /* Downmix.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */; };
|
||||
8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */; };
|
||||
834A41A9287A90AB00EB9D9B /* freesurround_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 834A41A5287A90AB00EB9D9B /* freesurround_decoder.h */; };
|
||||
834A41AA287A90AB00EB9D9B /* freesurround_decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 834A41A6287A90AB00EB9D9B /* freesurround_decoder.cpp */; };
|
||||
834A41AB287A90AB00EB9D9B /* channelmaps.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 834A41A7287A90AB00EB9D9B /* channelmaps.cpp */; };
|
||||
834A41AC287A90AB00EB9D9B /* channelmaps.h in Headers */ = {isa = PBXBuildFile; fileRef = 834A41A8287A90AB00EB9D9B /* channelmaps.h */; };
|
||||
834A41AF287ABD6F00EB9D9B /* FSurroundFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 834A41AD287ABD6F00EB9D9B /* FSurroundFilter.h */; };
|
||||
834A41B0287ABD6F00EB9D9B /* FSurroundFilter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 834A41AE287ABD6F00EB9D9B /* FSurroundFilter.mm */; };
|
||||
834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EA27AF8F380063BC83 /* AudioChunk.h */; };
|
||||
834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EA27AF8F380063BC83 /* AudioChunk.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
834FD4ED27AF91220063BC83 /* AudioChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 834FD4EC27AF91220063BC83 /* AudioChunk.m */; };
|
||||
834FD4F027AF93680063BC83 /* ChunkList.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EE27AF93680063BC83 /* ChunkList.h */; };
|
||||
834FD4F027AF93680063BC83 /* ChunkList.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EE27AF93680063BC83 /* ChunkList.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
834FD4F127AF93680063BC83 /* ChunkList.m in Sources */ = {isa = PBXBuildFile; fileRef = 834FD4EF27AF93680063BC83 /* ChunkList.m */; };
|
||||
83504165286447DA006B32CC /* Downmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 83504163286447DA006B32CC /* Downmix.h */; };
|
||||
83504166286447DA006B32CC /* Downmix.m in Sources */ = {isa = PBXBuildFile; fileRef = 83504164286447DA006B32CC /* Downmix.m */; };
|
||||
8350416D28646149006B32CC /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8350416C28646149006B32CC /* CoreMedia.framework */; };
|
||||
835C88B1279811A500E28EAE /* hdcd_decode2.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88AF279811A500E28EAE /* hdcd_decode2.h */; };
|
||||
835C88B2279811A500E28EAE /* hdcd_decode2.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C88B0279811A500E28EAE /* hdcd_decode2.c */; };
|
||||
835DD2672ACAF1D90057E319 /* OutputCoreAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 835DD2652ACAF1D90057E319 /* OutputCoreAudio.m */; };
|
||||
835DD2682ACAF1D90057E319 /* OutputCoreAudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 835DD2662ACAF1D90057E319 /* OutputCoreAudio.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
835DD2722ACAF5AD0057E319 /* lpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 835DD26D2ACAF5AD0057E319 /* lpc.h */; };
|
||||
835DD2732ACAF5AD0057E319 /* util.h in Headers */ = {isa = PBXBuildFile; fileRef = 835DD26E2ACAF5AD0057E319 /* util.h */; };
|
||||
835DD2742ACAF5AD0057E319 /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 835DD26F2ACAF5AD0057E319 /* lpc.c */; };
|
||||
835FAC5E27BCA14D00BA8562 /* BadSampleCleaner.h in Headers */ = {isa = PBXBuildFile; fileRef = 835FAC5C27BCA14D00BA8562 /* BadSampleCleaner.h */; };
|
||||
835FAC5F27BCA14D00BA8562 /* BadSampleCleaner.m in Sources */ = {isa = PBXBuildFile; fileRef = 835FAC5D27BCA14D00BA8562 /* BadSampleCleaner.m */; };
|
||||
836DF618298F6F5F00CD0580 /* libsoxr.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 836DF615298F6E8900CD0580 /* libsoxr.0.dylib */; };
|
||||
83725A9027AA16C90003F694 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83725A7B27AA0D8A0003F694 /* Accelerate.framework */; };
|
||||
83725A9127AA16D50003F694 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83725A7C27AA0D8E0003F694 /* AVFoundation.framework */; };
|
||||
8377C64C27B8C51500E8BC0F /* fft_accelerate.c in Sources */ = {isa = PBXBuildFile; fileRef = 8377C64B27B8C51500E8BC0F /* fft_accelerate.c */; };
|
||||
8377C64E27B8C54400E8BC0F /* fft.h in Headers */ = {isa = PBXBuildFile; fileRef = 8377C64D27B8C54400E8BC0F /* fft.h */; };
|
||||
8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; };
|
||||
838A33722D06A97D00D0D770 /* librubberband.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 838A33712D06A97D00D0D770 /* librubberband.3.dylib */; };
|
||||
839065F32853338700636FBB /* dsd2float.h in Headers */ = {isa = PBXBuildFile; fileRef = 839065F22853338700636FBB /* dsd2float.h */; };
|
||||
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
|
||||
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
|
||||
8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */ = {isa = PBXBuildFile; fileRef = 8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */; };
|
||||
8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */; };
|
||||
839B83FA286D91ED00F529EE /* VisualizationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839B83F9286D91ED00F529EE /* VisualizationController.swift */; };
|
||||
839E56E52879450300DFB5F4 /* HrtfData.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E12879450300DFB5F4 /* HrtfData.h */; };
|
||||
839E56E62879450300DFB5F4 /* Endianness.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E22879450300DFB5F4 /* Endianness.h */; };
|
||||
839E56E72879450300DFB5F4 /* HrtfData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 839E56E32879450300DFB5F4 /* HrtfData.cpp */; };
|
||||
839E56E82879450300DFB5F4 /* IHrtfData.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E42879450300DFB5F4 /* IHrtfData.h */; };
|
||||
839E56EA28794F6300DFB5F4 /* HrtfTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56E928794F6300DFB5F4 /* HrtfTypes.h */; };
|
||||
839E56ED2879515D00DFB5F4 /* HeadphoneFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56EB2879515D00DFB5F4 /* HeadphoneFilter.h */; };
|
||||
839E56EE2879515D00DFB5F4 /* HeadphoneFilter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 839E56EC2879515D00DFB5F4 /* HeadphoneFilter.mm */; };
|
||||
839E56F7287974A100DFB5F4 /* SandboxBroker.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E56F6287974A100DFB5F4 /* SandboxBroker.h */; };
|
||||
839E899E2D5DB9D500A13526 /* VisualizationNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E899D2D5DB9D500A13526 /* VisualizationNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
839E89A02D5DBA1700A13526 /* VisualizationNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 839E899F2D5DBA1700A13526 /* VisualizationNode.m */; };
|
||||
83A3496A2D5C3F430096D530 /* DSPRubberbandNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A349682D5C3F430096D530 /* DSPRubberbandNode.m */; };
|
||||
83A3496B2D5C3F430096D530 /* DSPRubberbandNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A349672D5C3F430096D530 /* DSPRubberbandNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83A3496D2D5C40490096D530 /* DSPFSurroundNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A3496C2D5C40490096D530 /* DSPFSurroundNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83A3496F2D5C405E0096D530 /* DSPFSurroundNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3496E2D5C405E0096D530 /* DSPFSurroundNode.m */; };
|
||||
83A349722D5C41810096D530 /* FSurroundFilter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83A349712D5C41810096D530 /* FSurroundFilter.mm */; };
|
||||
83A349732D5C41810096D530 /* FSurroundFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A349702D5C41810096D530 /* FSurroundFilter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83A349752D5C50A10096D530 /* DSPHRTFNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A349742D5C50A10096D530 /* DSPHRTFNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83A349772D5C50B20096D530 /* DSPHRTFNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A349762D5C50B20096D530 /* DSPHRTFNode.m */; };
|
||||
83B74281289E027F005AAC28 /* CogAudio-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B74280289E027F005AAC28 /* CogAudio-Bridging-Header.h */; };
|
||||
83CB56652E06464D003DD379 /* NSDictionary+Optional.h in Headers */ = {isa = PBXBuildFile; fileRef = 83CB56632E06464D003DD379 /* NSDictionary+Optional.h */; };
|
||||
83CB56662E06464D003DD379 /* NSDictionary+Optional.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CB56642E06464D003DD379 /* NSDictionary+Optional.m */; };
|
||||
83F843202D5C6272008C123B /* HeadphoneFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F8431E2D5C6272008C123B /* HeadphoneFilter.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83F843212D5C6272008C123B /* HeadphoneFilter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83F8431F2D5C6272008C123B /* HeadphoneFilter.mm */; };
|
||||
83F843232D5C66DA008C123B /* DSPEqualizerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F843222D5C66DA008C123B /* DSPEqualizerNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83F843252D5C66E9008C123B /* DSPEqualizerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F843242D5C66E9008C123B /* DSPEqualizerNode.m */; };
|
||||
83F9FFF62D6EC43900026576 /* soxr.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F9FFF02D6EC43900026576 /* soxr.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83F9FFF82D6EC43900026576 /* libsoxr.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83F9FFF22D6EC43900026576 /* libsoxr.0.dylib */; };
|
||||
83FFED512D5B08BC0044CCAF /* DSPNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FFED502D5B08BC0044CCAF /* DSPNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
83FFED532D5B09320044CCAF /* DSPNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83FFED522D5B09320044CCAF /* DSPNode.m */; };
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
||||
8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; };
|
||||
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8EC1225D0B993BD500C5B3AD /* ConverterNode.h */; };
|
||||
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8EC1225D0B993BD500C5B3AD /* ConverterNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EC1225E0B993BD500C5B3AD /* ConverterNode.m */; };
|
||||
B0575F2D0D687A0800411D77 /* Helper.h in Headers */ = {isa = PBXBuildFile; fileRef = B0575F2C0D687A0800411D77 /* Helper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B0575F300D687A4000411D77 /* Helper.m in Sources */ = {isa = PBXBuildFile; fileRef = B0575F2F0D687A4000411D77 /* Helper.m */; };
|
||||
|
@ -132,8 +154,6 @@
|
|||
17D21C7D0B8BE4BA00D1EBDE /* Node.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Node.m; sourceTree = "<group>"; };
|
||||
17D21C7E0B8BE4BA00D1EBDE /* OutputNode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputNode.h; sourceTree = "<group>"; };
|
||||
17D21C7F0B8BE4BA00D1EBDE /* OutputNode.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputNode.m; sourceTree = "<group>"; };
|
||||
17D21C9C0B8BE4BA00D1EBDE /* OutputAVFoundation.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputAVFoundation.h; sourceTree = "<group>"; };
|
||||
17D21C9D0B8BE4BA00D1EBDE /* OutputAVFoundation.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputAVFoundation.m; sourceTree = "<group>"; };
|
||||
17D21C9E0B8BE4BA00D1EBDE /* Status.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Status.h; sourceTree = "<group>"; };
|
||||
17D21CF10B8BE5EF00D1EBDE /* CogSemaphore.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CogSemaphore.h; sourceTree = "<group>"; };
|
||||
17D21CF20B8BE5EF00D1EBDE /* CogSemaphore.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = CogSemaphore.m; sourceTree = "<group>"; };
|
||||
|
@ -157,46 +177,73 @@
|
|||
8328995527CB51B700D7F028 /* SHA256Digest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SHA256Digest.h; path = ../../Utils/SHA256Digest.h; sourceTree = "<group>"; };
|
||||
8328995627CB51B700D7F028 /* SHA256Digest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SHA256Digest.m; path = ../../Utils/SHA256Digest.m; sourceTree = "<group>"; };
|
||||
8328995927CB51C900D7F028 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
833442402D6EFA6700C51D38 /* VisualizationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VisualizationController.h; sourceTree = "<group>"; };
|
||||
833442412D6EFA6700C51D38 /* VisualizationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VisualizationController.m; sourceTree = "<group>"; };
|
||||
833738E92D5EA52500278628 /* DSPDownmixNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPDownmixNode.h; sourceTree = "<group>"; };
|
||||
833738EB2D5EA53500278628 /* DSPDownmixNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPDownmixNode.m; sourceTree = "<group>"; };
|
||||
833738ED2D5EA5B700278628 /* Downmix.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Downmix.h; sourceTree = "<group>"; };
|
||||
833738EE2D5EA5B700278628 /* Downmix.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Downmix.m; sourceTree = "<group>"; };
|
||||
8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSFileHandle+CreateFile.h"; path = "../../Utils/NSFileHandle+CreateFile.h"; sourceTree = "<group>"; };
|
||||
8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSFileHandle+CreateFile.m"; path = "../../Utils/NSFileHandle+CreateFile.m"; sourceTree = "<group>"; };
|
||||
834A41A5287A90AB00EB9D9B /* freesurround_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = freesurround_decoder.h; sourceTree = "<group>"; };
|
||||
834A41A6287A90AB00EB9D9B /* freesurround_decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = freesurround_decoder.cpp; sourceTree = "<group>"; };
|
||||
834A41A7287A90AB00EB9D9B /* channelmaps.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = channelmaps.cpp; sourceTree = "<group>"; };
|
||||
834A41A8287A90AB00EB9D9B /* channelmaps.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = channelmaps.h; sourceTree = "<group>"; };
|
||||
834A41AD287ABD6F00EB9D9B /* FSurroundFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSurroundFilter.h; sourceTree = "<group>"; };
|
||||
834A41AE287ABD6F00EB9D9B /* FSurroundFilter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSurroundFilter.mm; sourceTree = "<group>"; };
|
||||
834FD4EA27AF8F380063BC83 /* AudioChunk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioChunk.h; sourceTree = "<group>"; };
|
||||
834FD4EC27AF91220063BC83 /* AudioChunk.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioChunk.m; sourceTree = "<group>"; };
|
||||
834FD4EE27AF93680063BC83 /* ChunkList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChunkList.h; sourceTree = "<group>"; };
|
||||
834FD4EF27AF93680063BC83 /* ChunkList.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChunkList.m; sourceTree = "<group>"; };
|
||||
83504163286447DA006B32CC /* Downmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downmix.h; sourceTree = "<group>"; };
|
||||
83504164286447DA006B32CC /* Downmix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downmix.m; sourceTree = "<group>"; };
|
||||
8350416C28646149006B32CC /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
|
||||
835C88AF279811A500E28EAE /* hdcd_decode2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hdcd_decode2.h; sourceTree = "<group>"; };
|
||||
835C88B0279811A500E28EAE /* hdcd_decode2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hdcd_decode2.c; sourceTree = "<group>"; };
|
||||
835DD2652ACAF1D90057E319 /* OutputCoreAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OutputCoreAudio.m; sourceTree = "<group>"; };
|
||||
835DD2662ACAF1D90057E319 /* OutputCoreAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OutputCoreAudio.h; sourceTree = "<group>"; };
|
||||
835DD26B2ACAF5AD0057E319 /* LICENSE.LGPL */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.LGPL; sourceTree = "<group>"; };
|
||||
835DD26C2ACAF5AD0057E319 /* License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
|
||||
835DD26D2ACAF5AD0057E319 /* lpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lpc.h; sourceTree = "<group>"; };
|
||||
835DD26E2ACAF5AD0057E319 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = "<group>"; };
|
||||
835DD26F2ACAF5AD0057E319 /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = "<group>"; };
|
||||
835FAC5C27BCA14D00BA8562 /* BadSampleCleaner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BadSampleCleaner.h; path = Utils/BadSampleCleaner.h; sourceTree = SOURCE_ROOT; };
|
||||
835FAC5D27BCA14D00BA8562 /* BadSampleCleaner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BadSampleCleaner.m; path = Utils/BadSampleCleaner.m; sourceTree = SOURCE_ROOT; };
|
||||
836DF615298F6E8900CD0580 /* libsoxr.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsoxr.0.dylib; path = ../ThirdParty/soxr/lib/libsoxr.0.dylib; sourceTree = "<group>"; };
|
||||
83725A7B27AA0D8A0003F694 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
|
||||
83725A7C27AA0D8E0003F694 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
8377C64B27B8C51500E8BC0F /* fft_accelerate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fft_accelerate.c; sourceTree = "<group>"; };
|
||||
8377C64D27B8C54400E8BC0F /* fft.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fft.h; sourceTree = "<group>"; };
|
||||
8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||
838A33712D06A97D00D0D770 /* librubberband.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librubberband.3.dylib; path = ../ThirdParty/rubberband/lib/librubberband.3.dylib; sourceTree = SOURCE_ROOT; };
|
||||
839065F22853338700636FBB /* dsd2float.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsd2float.h; sourceTree = "<group>"; };
|
||||
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
|
||||
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
|
||||
8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Merge.h"; path = "../../Utils/NSDictionary+Merge.h"; sourceTree = "<group>"; };
|
||||
8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge.m"; path = "../../Utils/NSDictionary+Merge.m"; sourceTree = "<group>"; };
|
||||
839B83F9286D91ED00F529EE /* VisualizationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualizationController.swift; sourceTree = "<group>"; };
|
||||
8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Merge.h"; path = "../Utils/NSDictionary+Merge.h"; sourceTree = SOURCE_ROOT; };
|
||||
8399CF2B27B5D1D4008751F1 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge.m"; path = "../Utils/NSDictionary+Merge.m"; sourceTree = SOURCE_ROOT; };
|
||||
839E56E12879450300DFB5F4 /* HrtfData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HrtfData.h; sourceTree = "<group>"; };
|
||||
839E56E22879450300DFB5F4 /* Endianness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Endianness.h; sourceTree = "<group>"; };
|
||||
839E56E32879450300DFB5F4 /* HrtfData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HrtfData.cpp; sourceTree = "<group>"; };
|
||||
839E56E42879450300DFB5F4 /* IHrtfData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IHrtfData.h; sourceTree = "<group>"; };
|
||||
839E56E928794F6300DFB5F4 /* HrtfTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HrtfTypes.h; sourceTree = "<group>"; };
|
||||
839E56EB2879515D00DFB5F4 /* HeadphoneFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeadphoneFilter.h; sourceTree = "<group>"; };
|
||||
839E56EC2879515D00DFB5F4 /* HeadphoneFilter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HeadphoneFilter.mm; sourceTree = "<group>"; };
|
||||
839E56F6287974A100DFB5F4 /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../Utils/SandboxBroker.h; sourceTree = "<group>"; };
|
||||
839E899D2D5DB9D500A13526 /* VisualizationNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VisualizationNode.h; sourceTree = "<group>"; };
|
||||
839E899F2D5DBA1700A13526 /* VisualizationNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VisualizationNode.m; sourceTree = "<group>"; };
|
||||
83A349672D5C3F430096D530 /* DSPRubberbandNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPRubberbandNode.h; sourceTree = "<group>"; };
|
||||
83A349682D5C3F430096D530 /* DSPRubberbandNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPRubberbandNode.m; sourceTree = "<group>"; };
|
||||
83A3496C2D5C40490096D530 /* DSPFSurroundNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPFSurroundNode.h; sourceTree = "<group>"; };
|
||||
83A3496E2D5C405E0096D530 /* DSPFSurroundNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPFSurroundNode.m; sourceTree = "<group>"; };
|
||||
83A349702D5C41810096D530 /* FSurroundFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSurroundFilter.h; sourceTree = "<group>"; };
|
||||
83A349712D5C41810096D530 /* FSurroundFilter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FSurroundFilter.mm; sourceTree = "<group>"; };
|
||||
83A349742D5C50A10096D530 /* DSPHRTFNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPHRTFNode.h; sourceTree = "<group>"; };
|
||||
83A349762D5C50B20096D530 /* DSPHRTFNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPHRTFNode.m; sourceTree = "<group>"; };
|
||||
83B74280289E027F005AAC28 /* CogAudio-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CogAudio-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
83CB56632E06464D003DD379 /* NSDictionary+Optional.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Optional.h"; path = "../Utils/NSDictionary+Optional.h"; sourceTree = SOURCE_ROOT; };
|
||||
83CB56642E06464D003DD379 /* NSDictionary+Optional.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Optional.m"; path = "../Utils/NSDictionary+Optional.m"; sourceTree = SOURCE_ROOT; };
|
||||
83F8431E2D5C6272008C123B /* HeadphoneFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HeadphoneFilter.h; sourceTree = "<group>"; };
|
||||
83F8431F2D5C6272008C123B /* HeadphoneFilter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HeadphoneFilter.mm; sourceTree = "<group>"; };
|
||||
83F843222D5C66DA008C123B /* DSPEqualizerNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPEqualizerNode.h; sourceTree = "<group>"; };
|
||||
83F843242D5C66E9008C123B /* DSPEqualizerNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPEqualizerNode.m; sourceTree = "<group>"; };
|
||||
83F9FFF02D6EC43900026576 /* soxr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = soxr.h; sourceTree = "<group>"; };
|
||||
83F9FFF22D6EC43900026576 /* libsoxr.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsoxr.0.dylib; sourceTree = "<group>"; };
|
||||
83F9FFF42D6EC43900026576 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
83FFED502D5B08BC0044CCAF /* DSPNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPNode.h; sourceTree = "<group>"; };
|
||||
83FFED522D5B09320044CCAF /* DSPNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPNode.m; sourceTree = "<group>"; };
|
||||
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = "<group>"; };
|
||||
|
@ -215,14 +262,15 @@
|
|||
files = (
|
||||
8328995A27CB51C900D7F028 /* Security.framework in Frameworks */,
|
||||
83725A9127AA16D50003F694 /* AVFoundation.framework in Frameworks */,
|
||||
83F9FFF82D6EC43900026576 /* libsoxr.0.dylib in Frameworks */,
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
|
||||
8350416D28646149006B32CC /* CoreMedia.framework in Frameworks */,
|
||||
83725A9027AA16C90003F694 /* Accelerate.framework in Frameworks */,
|
||||
17D21DAD0B8BE76800D1EBDE /* AudioToolbox.framework in Frameworks */,
|
||||
836DF618298F6F5F00CD0580 /* libsoxr.0.dylib in Frameworks */,
|
||||
17D21DAE0B8BE76800D1EBDE /* AudioUnit.framework in Frameworks */,
|
||||
17D21DAF0B8BE76800D1EBDE /* CoreAudio.framework in Frameworks */,
|
||||
17D21DB00B8BE76800D1EBDE /* CoreAudioKit.framework in Frameworks */,
|
||||
838A33722D06A97D00D0D770 /* librubberband.3.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -314,7 +362,7 @@
|
|||
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
836DF615298F6E8900CD0580 /* libsoxr.0.dylib */,
|
||||
838A33712D06A97D00D0D770 /* librubberband.3.dylib */,
|
||||
83725A7B27AA0D8A0003F694 /* Accelerate.framework */,
|
||||
17D21DAA0B8BE76800D1EBDE /* AudioUnit.framework */,
|
||||
17D21DA90B8BE76800D1EBDE /* AudioToolbox.framework */,
|
||||
|
@ -331,6 +379,7 @@
|
|||
17D21C750B8BE4BA00D1EBDE /* Chain */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83A349692D5C3F430096D530 /* DSP */,
|
||||
834FD4EA27AF8F380063BC83 /* AudioChunk.h */,
|
||||
834FD4EC27AF91220063BC83 /* AudioChunk.m */,
|
||||
17D21C760B8BE4BA00D1EBDE /* BufferChain.h */,
|
||||
|
@ -339,14 +388,16 @@
|
|||
834FD4EF27AF93680063BC83 /* ChunkList.m */,
|
||||
8EC1225D0B993BD500C5B3AD /* ConverterNode.h */,
|
||||
8EC1225E0B993BD500C5B3AD /* ConverterNode.m */,
|
||||
83504163286447DA006B32CC /* Downmix.h */,
|
||||
83504164286447DA006B32CC /* Downmix.m */,
|
||||
17D21C7A0B8BE4BA00D1EBDE /* InputNode.h */,
|
||||
17D21C7B0B8BE4BA00D1EBDE /* InputNode.m */,
|
||||
17D21C7C0B8BE4BA00D1EBDE /* Node.h */,
|
||||
17D21C7D0B8BE4BA00D1EBDE /* Node.m */,
|
||||
17D21C7E0B8BE4BA00D1EBDE /* OutputNode.h */,
|
||||
17D21C7F0B8BE4BA00D1EBDE /* OutputNode.m */,
|
||||
83FFED502D5B08BC0044CCAF /* DSPNode.h */,
|
||||
83FFED522D5B09320044CCAF /* DSPNode.m */,
|
||||
839E899D2D5DB9D500A13526 /* VisualizationNode.h */,
|
||||
839E899F2D5DBA1700A13526 /* VisualizationNode.m */,
|
||||
);
|
||||
path = Chain;
|
||||
sourceTree = "<group>";
|
||||
|
@ -354,12 +405,8 @@
|
|||
17D21C9B0B8BE4BA00D1EBDE /* Output */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
834A41AD287ABD6F00EB9D9B /* FSurroundFilter.h */,
|
||||
834A41AE287ABD6F00EB9D9B /* FSurroundFilter.mm */,
|
||||
839E56EB2879515D00DFB5F4 /* HeadphoneFilter.h */,
|
||||
839E56EC2879515D00DFB5F4 /* HeadphoneFilter.mm */,
|
||||
17D21C9C0B8BE4BA00D1EBDE /* OutputAVFoundation.h */,
|
||||
17D21C9D0B8BE4BA00D1EBDE /* OutputAVFoundation.m */,
|
||||
835DD2662ACAF1D90057E319 /* OutputCoreAudio.h */,
|
||||
835DD2652ACAF1D90057E319 /* OutputCoreAudio.m */,
|
||||
);
|
||||
path = Output;
|
||||
sourceTree = "<group>";
|
||||
|
@ -367,6 +414,8 @@
|
|||
17D21CD80B8BE5B400D1EBDE /* ThirdParty */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83F9FFF52D6EC43900026576 /* soxr */,
|
||||
835DD2692ACAF5AD0057E319 /* lvqcl */,
|
||||
834A41A4287A90AB00EB9D9B /* fsurround */,
|
||||
839E56E02879450300DFB5F4 /* hrtf */,
|
||||
831A50152865A8800049CFE4 /* rsstate.cpp */,
|
||||
|
@ -382,6 +431,8 @@
|
|||
17D21CDC0B8BE5B400D1EBDE /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83CB56632E06464D003DD379 /* NSDictionary+Optional.h */,
|
||||
83CB56642E06464D003DD379 /* NSDictionary+Optional.m */,
|
||||
839065F22853338700636FBB /* dsd2float.h */,
|
||||
8328995527CB51B700D7F028 /* SHA256Digest.h */,
|
||||
8328995627CB51B700D7F028 /* SHA256Digest.m */,
|
||||
|
@ -437,6 +488,26 @@
|
|||
path = hdcd;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
835DD2692ACAF5AD0057E319 /* lvqcl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
835DD26A2ACAF5AD0057E319 /* License */,
|
||||
835DD26D2ACAF5AD0057E319 /* lpc.h */,
|
||||
835DD26E2ACAF5AD0057E319 /* util.h */,
|
||||
835DD26F2ACAF5AD0057E319 /* lpc.c */,
|
||||
);
|
||||
path = lvqcl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
835DD26A2ACAF5AD0057E319 /* License */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
835DD26B2ACAF5AD0057E319 /* LICENSE.LGPL */,
|
||||
835DD26C2ACAF5AD0057E319 /* License.txt */,
|
||||
);
|
||||
path = License;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83725A8F27AA16C90003F694 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -458,7 +529,8 @@
|
|||
8377C64F27B8CAAB00E8BC0F /* Visualization */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
839B83F9286D91ED00F529EE /* VisualizationController.swift */,
|
||||
833442402D6EFA6700C51D38 /* VisualizationController.h */,
|
||||
833442412D6EFA6700C51D38 /* VisualizationController.m */,
|
||||
);
|
||||
path = Visualization;
|
||||
sourceTree = "<group>";
|
||||
|
@ -475,6 +547,56 @@
|
|||
path = hrtf;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83A349692D5C3F430096D530 /* DSP */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
833738ED2D5EA5B700278628 /* Downmix.h */,
|
||||
833738EE2D5EA5B700278628 /* Downmix.m */,
|
||||
83F8431E2D5C6272008C123B /* HeadphoneFilter.h */,
|
||||
83F8431F2D5C6272008C123B /* HeadphoneFilter.mm */,
|
||||
83A349702D5C41810096D530 /* FSurroundFilter.h */,
|
||||
83A349712D5C41810096D530 /* FSurroundFilter.mm */,
|
||||
83A349672D5C3F430096D530 /* DSPRubberbandNode.h */,
|
||||
83A349682D5C3F430096D530 /* DSPRubberbandNode.m */,
|
||||
83A3496C2D5C40490096D530 /* DSPFSurroundNode.h */,
|
||||
83A3496E2D5C405E0096D530 /* DSPFSurroundNode.m */,
|
||||
83A349742D5C50A10096D530 /* DSPHRTFNode.h */,
|
||||
83A349762D5C50B20096D530 /* DSPHRTFNode.m */,
|
||||
83F843222D5C66DA008C123B /* DSPEqualizerNode.h */,
|
||||
83F843242D5C66E9008C123B /* DSPEqualizerNode.m */,
|
||||
833738E92D5EA52500278628 /* DSPDownmixNode.h */,
|
||||
833738EB2D5EA53500278628 /* DSPDownmixNode.m */,
|
||||
);
|
||||
path = DSP;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83F9FFF12D6EC43900026576 /* include */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83F9FFF02D6EC43900026576 /* soxr.h */,
|
||||
);
|
||||
path = include;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83F9FFF32D6EC43900026576 /* lib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83F9FFF22D6EC43900026576 /* libsoxr.0.dylib */,
|
||||
);
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83F9FFF52D6EC43900026576 /* soxr */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83F9FFF12D6EC43900026576 /* include */,
|
||||
83F9FFF32D6EC43900026576 /* lib */,
|
||||
83F9FFF42D6EC43900026576 /* README.md */,
|
||||
);
|
||||
name = soxr;
|
||||
path = ../ThirdParty/soxr;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
|
@ -482,35 +604,48 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
833442422D6EFA6700C51D38 /* VisualizationController.h in Headers */,
|
||||
833738F02D5EA5B700278628 /* Downmix.h in Headers */,
|
||||
834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */,
|
||||
83F843202D5C6272008C123B /* HeadphoneFilter.h in Headers */,
|
||||
83A349732D5C41810096D530 /* FSurroundFilter.h in Headers */,
|
||||
839E56E82879450300DFB5F4 /* IHrtfData.h in Headers */,
|
||||
17D21CA10B8BE4BA00D1EBDE /* BufferChain.h in Headers */,
|
||||
831A50142865A7FD0049CFE4 /* rsstate.hpp in Headers */,
|
||||
835DD2682ACAF1D90057E319 /* OutputCoreAudio.h in Headers */,
|
||||
834A41AC287A90AB00EB9D9B /* channelmaps.h in Headers */,
|
||||
83A3496D2D5C40490096D530 /* DSPFSurroundNode.h in Headers */,
|
||||
83A3496B2D5C3F430096D530 /* DSPRubberbandNode.h in Headers */,
|
||||
17D21CA50B8BE4BA00D1EBDE /* InputNode.h in Headers */,
|
||||
83CB56652E06464D003DD379 /* NSDictionary+Optional.h in Headers */,
|
||||
833738EA2D5EA52500278628 /* DSPDownmixNode.h in Headers */,
|
||||
83F843232D5C66DA008C123B /* DSPEqualizerNode.h in Headers */,
|
||||
834A41A9287A90AB00EB9D9B /* freesurround_decoder.h in Headers */,
|
||||
834FD4F027AF93680063BC83 /* ChunkList.h in Headers */,
|
||||
835DD2732ACAF5AD0057E319 /* util.h in Headers */,
|
||||
17D21CA70B8BE4BA00D1EBDE /* Node.h in Headers */,
|
||||
8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */,
|
||||
17D21CA90B8BE4BA00D1EBDE /* OutputNode.h in Headers */,
|
||||
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */,
|
||||
8328995427CB511000D7F028 /* RedundantPlaylistDataStore.h in Headers */,
|
||||
17D21CC50B8BE4BA00D1EBDE /* OutputAVFoundation.h in Headers */,
|
||||
83504165286447DA006B32CC /* Downmix.h in Headers */,
|
||||
839E56E52879450300DFB5F4 /* HrtfData.h in Headers */,
|
||||
83B74281289E027F005AAC28 /* CogAudio-Bridging-Header.h in Headers */,
|
||||
83FFED512D5B08BC0044CCAF /* DSPNode.h in Headers */,
|
||||
839E899E2D5DB9D500A13526 /* VisualizationNode.h in Headers */,
|
||||
83A349752D5C50A10096D530 /* DSPHRTFNode.h in Headers */,
|
||||
83F9FFF62D6EC43900026576 /* soxr.h in Headers */,
|
||||
17D21CC70B8BE4BA00D1EBDE /* Status.h in Headers */,
|
||||
17D21CF30B8BE5EF00D1EBDE /* CogSemaphore.h in Headers */,
|
||||
839E56E62879450300DFB5F4 /* Endianness.h in Headers */,
|
||||
17D21DC70B8BE79700D1EBDE /* CoreAudioUtils.h in Headers */,
|
||||
839E56ED2879515D00DFB5F4 /* HeadphoneFilter.h in Headers */,
|
||||
835DD2722ACAF5AD0057E319 /* lpc.h in Headers */,
|
||||
17D21EBD0B8BF44000D1EBDE /* AudioPlayer.h in Headers */,
|
||||
831A50182865A8B30049CFE4 /* rsstate.h in Headers */,
|
||||
834FD4F027AF93680063BC83 /* ChunkList.h in Headers */,
|
||||
17F94DD50B8D0F7000A34E87 /* PluginController.h in Headers */,
|
||||
17F94DDD0B8D101100A34E87 /* Plugin.h in Headers */,
|
||||
8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */,
|
||||
834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */,
|
||||
834A41AF287ABD6F00EB9D9B /* FSurroundFilter.h in Headers */,
|
||||
17A2D3C50B8D1D37000778C4 /* AudioDecoder.h in Headers */,
|
||||
8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */,
|
||||
83B74281289E027F005AAC28 /* CogAudio-Bridging-Header.h in Headers */,
|
||||
17C940230B900909008627D6 /* AudioMetadataReader.h in Headers */,
|
||||
839E56F7287974A100DFB5F4 /* SandboxBroker.h in Headers */,
|
||||
839065F32853338700636FBB /* dsd2float.h in Headers */,
|
||||
|
@ -518,7 +653,6 @@
|
|||
839366671815923C006DD712 /* CogPluginMulti.h in Headers */,
|
||||
17ADB13C0B97926D00257CA2 /* AudioSource.h in Headers */,
|
||||
835C88B1279811A500E28EAE /* hdcd_decode2.h in Headers */,
|
||||
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */,
|
||||
8384912718080FF100E7332D /* Logging.h in Headers */,
|
||||
8377C64E27B8C54400E8BC0F /* fft.h in Headers */,
|
||||
835FAC5E27BCA14D00BA8562 /* BadSampleCleaner.h in Headers */,
|
||||
|
@ -532,9 +666,9 @@
|
|||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8DC2EF4F0486A6940098B216 /* CogAudio Framework */ = {
|
||||
8DC2EF4F0486A6940098B216 /* CogAudio */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "CogAudio Framework" */;
|
||||
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "CogAudio" */;
|
||||
buildPhases = (
|
||||
8DC2EF500486A6940098B216 /* Headers */,
|
||||
8DC2EF540486A6940098B216 /* Sources */,
|
||||
|
@ -546,7 +680,7 @@
|
|||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "CogAudio Framework";
|
||||
name = CogAudio;
|
||||
productInstallPath = "$(HOME)/Library/Frameworks";
|
||||
productName = CogAudio;
|
||||
productReference = 8DC2EF5B0486A6940098B216 /* CogAudio.framework */;
|
||||
|
@ -558,16 +692,11 @@
|
|||
0867D690FE84028FC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
TargetAttributes = {
|
||||
8DC2EF4F0486A6940098B216 = {
|
||||
LastSwiftMigration = 1330;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1620;
|
||||
};
|
||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "CogAudio" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
|
@ -579,7 +708,7 @@
|
|||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8DC2EF4F0486A6940098B216 /* CogAudio Framework */,
|
||||
8DC2EF4F0486A6940098B216 /* CogAudio */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -600,21 +729,25 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
17D21CA20B8BE4BA00D1EBDE /* BufferChain.m in Sources */,
|
||||
83A349772D5C50B20096D530 /* DSPHRTFNode.m in Sources */,
|
||||
17D21CA60B8BE4BA00D1EBDE /* InputNode.m in Sources */,
|
||||
839E56EE2879515D00DFB5F4 /* HeadphoneFilter.mm in Sources */,
|
||||
83504166286447DA006B32CC /* Downmix.m in Sources */,
|
||||
83A3496A2D5C3F430096D530 /* DSPRubberbandNode.m in Sources */,
|
||||
8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */,
|
||||
83F843252D5C66E9008C123B /* DSPEqualizerNode.m in Sources */,
|
||||
834A41AB287A90AB00EB9D9B /* channelmaps.cpp in Sources */,
|
||||
833738EC2D5EA53500278628 /* DSPDownmixNode.m in Sources */,
|
||||
833442432D6EFA6700C51D38 /* VisualizationController.m in Sources */,
|
||||
831A50162865A8800049CFE4 /* rsstate.cpp in Sources */,
|
||||
17D21CA80B8BE4BA00D1EBDE /* Node.m in Sources */,
|
||||
17D21CAA0B8BE4BA00D1EBDE /* OutputNode.m in Sources */,
|
||||
17D21CC60B8BE4BA00D1EBDE /* OutputAVFoundation.m in Sources */,
|
||||
835C88B2279811A500E28EAE /* hdcd_decode2.c in Sources */,
|
||||
835FAC5F27BCA14D00BA8562 /* BadSampleCleaner.m in Sources */,
|
||||
834FD4ED27AF91220063BC83 /* AudioChunk.m in Sources */,
|
||||
833738EF2D5EA5B700278628 /* Downmix.m in Sources */,
|
||||
17D21CF40B8BE5EF00D1EBDE /* CogSemaphore.m in Sources */,
|
||||
839B83FA286D91ED00F529EE /* VisualizationController.swift in Sources */,
|
||||
839E89A02D5DBA1700A13526 /* VisualizationNode.m in Sources */,
|
||||
8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */,
|
||||
83A3496F2D5C405E0096D530 /* DSPFSurroundNode.m in Sources */,
|
||||
17D21DC80B8BE79700D1EBDE /* CoreAudioUtils.m in Sources */,
|
||||
8328995327CB511000D7F028 /* RedundantPlaylistDataStore.m in Sources */,
|
||||
8377C64C27B8C51500E8BC0F /* fft_accelerate.c in Sources */,
|
||||
|
@ -626,12 +759,17 @@
|
|||
8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */,
|
||||
17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */,
|
||||
17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */,
|
||||
83F843212D5C6272008C123B /* HeadphoneFilter.mm in Sources */,
|
||||
17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */,
|
||||
834FD4F127AF93680063BC83 /* ChunkList.m in Sources */,
|
||||
834A41B0287ABD6F00EB9D9B /* FSurroundFilter.mm in Sources */,
|
||||
83FFED532D5B09320044CCAF /* DSPNode.m in Sources */,
|
||||
8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */,
|
||||
835DD2672ACAF1D90057E319 /* OutputCoreAudio.m in Sources */,
|
||||
83A349722D5C41810096D530 /* FSurroundFilter.mm in Sources */,
|
||||
83CB56662E06464D003DD379 /* NSDictionary+Optional.m in Sources */,
|
||||
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */,
|
||||
B0575F300D687A4000411D77 /* Helper.m in Sources */,
|
||||
835DD2742ACAF5AD0057E319 /* lpc.c in Sources */,
|
||||
834A41AA287A90AB00EB9D9B /* freesurround_decoder.cpp in Sources */,
|
||||
07DB5F3F0ED353A900C2E3EF /* AudioMetadataWriter.m in Sources */,
|
||||
);
|
||||
|
@ -645,12 +783,13 @@
|
|||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
|
@ -658,16 +797,22 @@
|
|||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = CogAudio_Prefix.pch;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
|
||||
HEADER_SEARCH_PATHS = ../ThirdParty/soxr/include;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../ThirdParty/soxr/include,
|
||||
../ThirdParty/rubberband/include,
|
||||
);
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "@executable_path/../Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = ../ThirdParty/soxr/lib;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
../ThirdParty/soxr/lib,
|
||||
../ThirdParty/rubberband/lib,
|
||||
);
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 c++17";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cogaudio;
|
||||
PRODUCT_NAME = CogAudio;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "CogAudio-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
WARNING_LDFLAGS = "";
|
||||
|
@ -680,26 +825,33 @@
|
|||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
ENABLE_MODULE_VERIFIER = YES;
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = CogAudio_Prefix.pch;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "";
|
||||
HEADER_SEARCH_PATHS = ../ThirdParty/soxr/include;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../ThirdParty/soxr/include,
|
||||
../ThirdParty/rubberband/include,
|
||||
);
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "@executable_path/../Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = ../ThirdParty/soxr/lib;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
../ThirdParty/soxr/lib,
|
||||
../ThirdParty/rubberband/lib,
|
||||
);
|
||||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 c++17";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cogaudio;
|
||||
PRODUCT_NAME = CogAudio;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "CogAudio-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
WARNING_LDFLAGS = "";
|
||||
WRAPPER_EXTENSION = framework;
|
||||
|
@ -735,6 +887,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
|
@ -746,11 +899,13 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
PRODUCT_MODULE_NAME = CogAudio;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "CogAudio-Bridging-Header.h";
|
||||
SYMROOT = ../build;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -783,6 +938,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "HAVE_CONFIG_H=1";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -791,10 +947,13 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
PRODUCT_MODULE_NAME = CogAudio;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "CogAudio-Bridging-Header.h";
|
||||
SYMROOT = ../build;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -802,7 +961,7 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "CogAudio Framework" */ = {
|
||||
1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "CogAudio" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB91AE08733DA50010E9CD /* Debug */,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -16,7 +16,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
|
||||
BuildableName = "CogAudio.framework"
|
||||
BlueprintName = "CogAudio Framework"
|
||||
BlueprintName = "CogAudio"
|
||||
ReferencedContainer = "container:CogAudio.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
|
@ -45,7 +45,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
|
||||
BuildableName = "CogAudio.framework"
|
||||
BlueprintName = "CogAudio Framework"
|
||||
BlueprintName = "CogAudio"
|
||||
ReferencedContainer = "container:CogAudio.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
|
@ -61,7 +61,7 @@
|
|||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
|
||||
BuildableName = "CogAudio.framework"
|
||||
BlueprintName = "CogAudio Framework"
|
||||
BlueprintName = "CogAudio"
|
||||
ReferencedContainer = "container:CogAudio.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
|
@ -1,516 +0,0 @@
|
|||
//
|
||||
// HeadphoneFilter.m
|
||||
// CogAudio Framework
|
||||
//
|
||||
// Created by Christopher Snowhill on 1/24/22.
|
||||
//
|
||||
|
||||
#import "HeadphoneFilter.h"
|
||||
#import "AudioChunk.h"
|
||||
#import "AudioDecoder.h"
|
||||
#import "AudioSource.h"
|
||||
|
||||
#import <stdlib.h>
|
||||
|
||||
#import <fstream>
|
||||
|
||||
#import "rsstate.h"
|
||||
|
||||
#import "HrtfData.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
typedef struct speakerPosition {
|
||||
float elevation;
|
||||
float azimuth;
|
||||
float distance;
|
||||
} speakerPosition;
|
||||
|
||||
#define DEGREES(x) ((x)*M_PI / 180.0)
|
||||
|
||||
static const speakerPosition speakerPositions[18] = {
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-135.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+135.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-15.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+15.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-180.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(-90.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(0.0), .azimuth = DEGREES(+90.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+90.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(-30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(+30.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(-135.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(0.0), .distance = 1.0 },
|
||||
{ .elevation = DEGREES(+45.0), .azimuth = DEGREES(+135.0), .distance = 1.0 }
|
||||
};
|
||||
|
||||
@interface impulseCacheObject : NSObject {
|
||||
}
|
||||
@property NSURL *URL;
|
||||
@property int sampleCount;
|
||||
@property int channelCount;
|
||||
@property uint32_t channelConfig;
|
||||
@property double sampleRate;
|
||||
@property double targetSampleRate;
|
||||
@property NSData *data;
|
||||
@end
|
||||
|
||||
@implementation impulseCacheObject
|
||||
@synthesize URL;
|
||||
@synthesize sampleCount;
|
||||
@synthesize channelCount;
|
||||
@synthesize channelConfig;
|
||||
@synthesize sampleRate;
|
||||
@synthesize targetSampleRate;
|
||||
@synthesize data;
|
||||
@end
|
||||
|
||||
@interface impulseCache : NSObject {
|
||||
}
|
||||
@property NSMutableArray<impulseCacheObject *> *cacheObjects;
|
||||
+ (impulseCache *)sharedController;
|
||||
- (const float *)getImpulse:(NSURL *)url sampleCount:(int *)sampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig sampleRate:(double)sampleRate;
|
||||
@end
|
||||
|
||||
// Apparently _mm_malloc is Intel-only on newer macOS targets, so use supported posix_memalign
|
||||
static void *_memalign_malloc(size_t size, size_t align) {
|
||||
void *ret = NULL;
|
||||
if(posix_memalign(&ret, align, size) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@implementation impulseCache
|
||||
|
||||
static impulseCache *_sharedController = nil;
|
||||
|
||||
+ (impulseCache *)sharedController {
|
||||
@synchronized(self) {
|
||||
if(!_sharedController) {
|
||||
_sharedController = [[impulseCache alloc] init];
|
||||
}
|
||||
}
|
||||
return _sharedController;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
self.cacheObjects = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (impulseCacheObject *)addImpulse:(NSURL *)url sampleCount:(int)sampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig originalSampleRate:(double)originalSampleRate targetSampleRate:(double)targetSampleRate impulseBuffer:(const float *)impulseBuffer {
|
||||
impulseCacheObject *obj = [[impulseCacheObject alloc] init];
|
||||
|
||||
obj.URL = url;
|
||||
obj.sampleCount = sampleCount;
|
||||
obj.channelCount = channelCount;
|
||||
obj.sampleRate = originalSampleRate;
|
||||
obj.targetSampleRate = targetSampleRate;
|
||||
obj.data = [NSData dataWithBytes:impulseBuffer length:(sampleCount * channelCount * sizeof(float) * 2)];
|
||||
|
||||
@synchronized(self.cacheObjects) {
|
||||
[self.cacheObjects addObject:obj];
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (const float *)getImpulse:(NSURL *)url sampleCount:(int *)retSampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig sampleRate:(double)sampleRate {
|
||||
BOOL impulseFound = NO;
|
||||
const float *impulseData = NULL;
|
||||
double sampleRateOfSource = 0;
|
||||
int sampleCount = 0;
|
||||
impulseCacheObject *cacheObject = nil;
|
||||
|
||||
@synchronized(self.cacheObjects) {
|
||||
for(impulseCacheObject *obj in self.cacheObjects) {
|
||||
if([obj.URL isEqualTo:url] &&
|
||||
obj.targetSampleRate == sampleRate &&
|
||||
obj.channelCount == channelCount &&
|
||||
obj.channelConfig == channelConfig) {
|
||||
*retSampleCount = obj.sampleCount;
|
||||
return (const float *)[obj.data bytes];
|
||||
}
|
||||
}
|
||||
for(impulseCacheObject *obj in self.cacheObjects) {
|
||||
if([obj.URL isEqualTo:url] &&
|
||||
obj.sampleRate == obj.targetSampleRate &&
|
||||
obj.channelCount == channelCount &&
|
||||
obj.channelConfig == channelConfig) {
|
||||
impulseData = (const float *)[obj.data bytes];
|
||||
sampleCount = obj.sampleCount;
|
||||
sampleRateOfSource = obj.sampleRate;
|
||||
impulseFound = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!impulseFound) {
|
||||
NSString *filePath = [url path];
|
||||
|
||||
try {
|
||||
std::ifstream file([filePath UTF8String], std::fstream::binary);
|
||||
|
||||
if(!file.is_open()) {
|
||||
throw std::logic_error("Cannot open file.");
|
||||
}
|
||||
|
||||
HrtfData data(file);
|
||||
|
||||
file.close();
|
||||
|
||||
sampleRateOfSource = data.get_sample_rate();
|
||||
|
||||
uint32_t sampleCountExact = data.get_response_length();
|
||||
sampleCount = sampleCountExact + ((data.get_longest_delay() + 2) >> 2);
|
||||
|
||||
std::vector<float> hrtfData(sampleCount * channelCount * 2, 0.0);
|
||||
|
||||
for(uint32_t i = 0; i < channelCount; ++i) {
|
||||
uint32_t channelFlag = [AudioChunk extractChannelFlag:i fromConfig:channelConfig];
|
||||
uint32_t channelNumber = [AudioChunk findChannelIndex:channelFlag];
|
||||
|
||||
if(channelNumber < 18) {
|
||||
const speakerPosition &speaker = speakerPositions[channelNumber];
|
||||
DirectionData hrtfLeft;
|
||||
DirectionData hrtfRight;
|
||||
|
||||
data.get_direction_data(speaker.elevation, speaker.azimuth, speaker.distance, hrtfLeft, hrtfRight);
|
||||
|
||||
cblas_scopy(sampleCountExact, &hrtfLeft.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) * channelCount * 2 + i * 2], channelCount * 2);
|
||||
cblas_scopy(sampleCountExact, &hrtfRight.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) * channelCount * 2 + i * 2 + 1], channelCount * 2);
|
||||
}
|
||||
}
|
||||
|
||||
cacheObject = [self addImpulse:url sampleCount:sampleCount channelCount:channelCount channelConfig:channelConfig originalSampleRate:sampleRateOfSource targetSampleRate:sampleRateOfSource impulseBuffer:&hrtfData[0]];
|
||||
|
||||
impulseData = (const float *)[cacheObject.data bytes];
|
||||
} catch(std::exception &e) {
|
||||
ALog(@"Exception caught: %s", e.what());
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if(sampleRateOfSource != sampleRate) {
|
||||
double sampleRatio = sampleRate / sampleRateOfSource;
|
||||
int resampledCount = (int)ceil((double)sampleCount * sampleRatio);
|
||||
|
||||
void *rsstate = rsstate_new(channelCount * 2, sampleRateOfSource, sampleRate);
|
||||
|
||||
float *resampledImpulse = (float *)_memalign_malloc(resampledCount * sizeof(float) * channelCount * 2, 16);
|
||||
if(!resampledImpulse) {
|
||||
rsstate_delete(rsstate);
|
||||
return nil;
|
||||
}
|
||||
|
||||
size_t inputDone = 0;
|
||||
size_t outputDone = 0;
|
||||
|
||||
outputDone = rsstate_resample(rsstate, impulseData, sampleCount, &inputDone, resampledImpulse, resampledCount);
|
||||
|
||||
while(outputDone < resampledCount) {
|
||||
outputDone += rsstate_flush(rsstate, resampledImpulse + outputDone * channelCount * 2, resampledCount - outputDone);
|
||||
}
|
||||
|
||||
rsstate_delete(rsstate);
|
||||
|
||||
sampleCount = (int)outputDone;
|
||||
|
||||
// Normalize resampled impulse by sample ratio
|
||||
float fSampleRatio = (float)sampleRatio;
|
||||
vDSP_vsdiv(resampledImpulse, 1, &fSampleRatio, resampledImpulse, 1, sampleCount * channelCount * 2);
|
||||
|
||||
cacheObject = [self addImpulse:url sampleCount:sampleCount channelCount:channelCount channelConfig:channelConfig originalSampleRate:sampleRateOfSource targetSampleRate:sampleRate impulseBuffer:resampledImpulse];
|
||||
|
||||
free(resampledImpulse);
|
||||
|
||||
impulseData = (const float *)[cacheObject.data bytes];
|
||||
}
|
||||
|
||||
*retSampleCount = sampleCount;
|
||||
return impulseData;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation HeadphoneFilter
|
||||
|
||||
+ (BOOL)validateImpulseFile:(NSURL *)url {
|
||||
NSString *filePath = [url path];
|
||||
|
||||
try {
|
||||
std::ifstream file([filePath UTF8String], std::fstream::binary);
|
||||
|
||||
if(!file.is_open()) {
|
||||
throw std::logic_error("Cannot open file.");
|
||||
}
|
||||
|
||||
HrtfData data(file);
|
||||
|
||||
file.close();
|
||||
|
||||
return YES;
|
||||
} catch(std::exception &e) {
|
||||
ALog(@"Exception thrown: %s", e.what());
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(int)channels withConfig:(uint32_t)config {
|
||||
self = [super init];
|
||||
|
||||
if(self) {
|
||||
int sampleCount = 0;
|
||||
const float *impulseBuffer = [[impulseCache sharedController] getImpulse:url sampleCount:&sampleCount channelCount:channels channelConfig:config sampleRate:sampleRate];
|
||||
if(!impulseBuffer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
channelCount = channels;
|
||||
|
||||
bufferSize = 512;
|
||||
fftSize = sampleCount + bufferSize;
|
||||
|
||||
int pow = 1;
|
||||
while(fftSize > 2) {
|
||||
pow++;
|
||||
fftSize /= 2;
|
||||
}
|
||||
fftSize = 2 << pow;
|
||||
|
||||
float *deinterleavedImpulseBuffer = (float *)_memalign_malloc(fftSize * sizeof(float) * channelCount * 2, 16);
|
||||
if(!deinterleavedImpulseBuffer) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
for(int i = 0; i < channelCount; ++i) {
|
||||
cblas_scopy(sampleCount, impulseBuffer + i * 2, (int)channelCount * 2, deinterleavedImpulseBuffer + i * fftSize * 2, 1);
|
||||
vDSP_vclr(deinterleavedImpulseBuffer + i * fftSize * 2 + sampleCount, 1, fftSize - sampleCount);
|
||||
cblas_scopy(sampleCount, impulseBuffer + i * 2 + 1, (int)channelCount * 2, deinterleavedImpulseBuffer + i * fftSize * 2 + fftSize, 1);
|
||||
vDSP_vclr(deinterleavedImpulseBuffer + i * fftSize * 2 + fftSize + sampleCount, 1, fftSize - sampleCount);
|
||||
}
|
||||
|
||||
paddedBufferSize = fftSize;
|
||||
fftSizeOver2 = (fftSize + 1) / 2;
|
||||
const int fftSizeOver2Plus1 = fftSizeOver2 + 1; // DFT float overwrites plus one, double doesn't
|
||||
|
||||
dftSetupF = vDSP_DFT_zrop_CreateSetup(nil, fftSize, vDSP_DFT_FORWARD);
|
||||
dftSetupB = vDSP_DFT_zrop_CreateSetup(nil, fftSize, vDSP_DFT_INVERSE);
|
||||
if(!dftSetupF || !dftSetupB) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
paddedSignal = (float *)_memalign_malloc(sizeof(float) * paddedBufferSize, 16);
|
||||
if(!paddedSignal) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
signal_fft.realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
signal_fft.imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
if(!signal_fft.realp || !signal_fft.imagp) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
input_filtered_signal_per_channel[0].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
input_filtered_signal_per_channel[0].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
if(!input_filtered_signal_per_channel[0].realp ||
|
||||
!input_filtered_signal_per_channel[0].imagp) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
input_filtered_signal_per_channel[1].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
input_filtered_signal_per_channel[1].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
if(!input_filtered_signal_per_channel[1].realp ||
|
||||
!input_filtered_signal_per_channel[1].imagp) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
input_filtered_signal_totals[0].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
input_filtered_signal_totals[0].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
if(!input_filtered_signal_totals[0].realp ||
|
||||
!input_filtered_signal_totals[0].imagp) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
input_filtered_signal_totals[1].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
input_filtered_signal_totals[1].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
if(!input_filtered_signal_totals[1].realp ||
|
||||
!input_filtered_signal_totals[1].imagp) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
impulse_responses = (DSPSplitComplex *)calloc(sizeof(DSPSplitComplex), channels * 2);
|
||||
if(!impulse_responses) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
for(int i = 0; i < channels; ++i) {
|
||||
impulse_responses[i * 2 + 0].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
impulse_responses[i * 2 + 0].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
impulse_responses[i * 2 + 1].realp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
impulse_responses[i * 2 + 1].imagp = (float *)_memalign_malloc(sizeof(float) * fftSizeOver2Plus1, 16);
|
||||
|
||||
if(!impulse_responses[i * 2 + 0].realp || !impulse_responses[i * 2 + 0].imagp ||
|
||||
!impulse_responses[i * 2 + 1].realp || !impulse_responses[i * 2 + 1].imagp) {
|
||||
free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + i * fftSize * 2), 2, &impulse_responses[i * 2 + 0], 1, fftSizeOver2);
|
||||
vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + i * fftSize * 2 + fftSize), 2, &impulse_responses[i * 2 + 1], 1, fftSizeOver2);
|
||||
|
||||
vDSP_DFT_Execute(dftSetupF, impulse_responses[i * 2 + 0].realp, impulse_responses[i * 2 + 0].imagp, impulse_responses[i * 2 + 0].realp, impulse_responses[i * 2 + 0].imagp);
|
||||
vDSP_DFT_Execute(dftSetupF, impulse_responses[i * 2 + 1].realp, impulse_responses[i * 2 + 1].imagp, impulse_responses[i * 2 + 1].realp, impulse_responses[i * 2 + 1].imagp);
|
||||
}
|
||||
|
||||
free(deinterleavedImpulseBuffer);
|
||||
|
||||
left_result = (float *)_memalign_malloc(sizeof(float) * fftSize, 16);
|
||||
right_result = (float *)_memalign_malloc(sizeof(float) * fftSize, 16);
|
||||
if(!left_result || !right_result)
|
||||
return nil;
|
||||
|
||||
prevInputs = (float **)calloc(channels, sizeof(float *));
|
||||
if(!prevInputs)
|
||||
return nil;
|
||||
for(int i = 0; i < channels; ++i) {
|
||||
prevInputs[i] = (float *)_memalign_malloc(sizeof(float) * fftSize, 16);
|
||||
if(!prevInputs[i])
|
||||
return nil;
|
||||
vDSP_vclr(prevInputs[i], 1, fftSize);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if(dftSetupF) vDSP_DFT_DestroySetup(dftSetupF);
|
||||
if(dftSetupB) vDSP_DFT_DestroySetup(dftSetupB);
|
||||
|
||||
free(paddedSignal);
|
||||
|
||||
free(signal_fft.realp);
|
||||
free(signal_fft.imagp);
|
||||
|
||||
free(input_filtered_signal_per_channel[0].realp);
|
||||
free(input_filtered_signal_per_channel[0].imagp);
|
||||
free(input_filtered_signal_per_channel[1].realp);
|
||||
free(input_filtered_signal_per_channel[1].imagp);
|
||||
|
||||
free(input_filtered_signal_totals[0].realp);
|
||||
free(input_filtered_signal_totals[0].imagp);
|
||||
free(input_filtered_signal_totals[1].realp);
|
||||
free(input_filtered_signal_totals[1].imagp);
|
||||
|
||||
if(impulse_responses) {
|
||||
for(int i = 0; i < channelCount * 2; ++i) {
|
||||
free(impulse_responses[i].realp);
|
||||
free(impulse_responses[i].imagp);
|
||||
}
|
||||
free(impulse_responses);
|
||||
}
|
||||
|
||||
free(left_result);
|
||||
free(right_result);
|
||||
|
||||
if(prevInputs) {
|
||||
for(int i = 0; i < channelCount; ++i) {
|
||||
free(prevInputs[i]);
|
||||
}
|
||||
free(prevInputs);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)process:(const float *)inBuffer sampleCount:(int)count toBuffer:(float *)outBuffer {
|
||||
const float scale = 1.0 / (4.0 * (float)fftSize);
|
||||
|
||||
while(count > 0) {
|
||||
const int countToDo = (count > bufferSize) ? bufferSize : count;
|
||||
const int prevToDo = fftSize - countToDo;
|
||||
|
||||
vDSP_vclr(input_filtered_signal_totals[0].realp, 1, fftSizeOver2);
|
||||
vDSP_vclr(input_filtered_signal_totals[0].imagp, 1, fftSizeOver2);
|
||||
vDSP_vclr(input_filtered_signal_totals[1].realp, 1, fftSizeOver2);
|
||||
vDSP_vclr(input_filtered_signal_totals[1].imagp, 1, fftSizeOver2);
|
||||
|
||||
for(int i = 0; i < channelCount; ++i) {
|
||||
cblas_scopy((int)prevToDo, prevInputs[i] + countToDo, 1, paddedSignal, 1);
|
||||
cblas_scopy((int)countToDo, inBuffer + i, (int)channelCount, paddedSignal + prevToDo, 1);
|
||||
cblas_scopy((int)fftSize, paddedSignal, 1, prevInputs[i], 1);
|
||||
|
||||
vDSP_ctoz((DSPComplex *)paddedSignal, 2, &signal_fft, 1, fftSizeOver2);
|
||||
|
||||
vDSP_DFT_Execute(dftSetupF, signal_fft.realp, signal_fft.imagp, signal_fft.realp, signal_fft.imagp);
|
||||
|
||||
// One channel forward, then multiply and back twice
|
||||
|
||||
float preserveIRNyq = impulse_responses[i * 2 + 0].imagp[0];
|
||||
float preserveSigNyq = signal_fft.imagp[0];
|
||||
impulse_responses[i * 2 + 0].imagp[0] = 0;
|
||||
signal_fft.imagp[0] = 0;
|
||||
|
||||
vDSP_zvmul(&signal_fft, 1, &impulse_responses[i * 2 + 0], 1, &input_filtered_signal_per_channel[0], 1, fftSizeOver2, 1);
|
||||
|
||||
input_filtered_signal_per_channel[0].imagp[0] = preserveIRNyq * preserveSigNyq;
|
||||
impulse_responses[i * 2 + 0].imagp[0] = preserveIRNyq;
|
||||
|
||||
preserveIRNyq = impulse_responses[i * 2 + 1].imagp[0];
|
||||
impulse_responses[i * 2 + 1].imagp[0] = 0;
|
||||
|
||||
vDSP_zvmul(&signal_fft, 1, &impulse_responses[i * 2 + 1], 1, &input_filtered_signal_per_channel[1], 1, fftSizeOver2, 1);
|
||||
|
||||
input_filtered_signal_per_channel[1].imagp[0] = preserveIRNyq * preserveSigNyq;
|
||||
impulse_responses[i * 2 + 1].imagp[0] = preserveIRNyq;
|
||||
|
||||
vDSP_zvadd(&input_filtered_signal_totals[0], 1, &input_filtered_signal_per_channel[0], 1, &input_filtered_signal_totals[0], 1, fftSizeOver2);
|
||||
vDSP_zvadd(&input_filtered_signal_totals[1], 1, &input_filtered_signal_per_channel[1], 1, &input_filtered_signal_totals[1], 1, fftSizeOver2);
|
||||
}
|
||||
|
||||
vDSP_DFT_Execute(dftSetupB, input_filtered_signal_totals[0].realp, input_filtered_signal_totals[0].imagp, input_filtered_signal_totals[0].realp, input_filtered_signal_totals[0].imagp);
|
||||
vDSP_DFT_Execute(dftSetupB, input_filtered_signal_totals[1].realp, input_filtered_signal_totals[1].imagp, input_filtered_signal_totals[1].realp, input_filtered_signal_totals[1].imagp);
|
||||
|
||||
vDSP_ztoc(&input_filtered_signal_totals[0], 1, (DSPComplex *)left_result, 2, fftSizeOver2);
|
||||
vDSP_ztoc(&input_filtered_signal_totals[1], 1, (DSPComplex *)right_result, 2, fftSizeOver2);
|
||||
|
||||
float *left_ptr = left_result + prevToDo;
|
||||
float *right_ptr = right_result + prevToDo;
|
||||
|
||||
vDSP_vsmul(left_ptr, 1, &scale, left_ptr, 1, countToDo);
|
||||
vDSP_vsmul(right_ptr, 1, &scale, right_ptr, 1, countToDo);
|
||||
|
||||
cblas_scopy((int)countToDo, left_ptr, 1, outBuffer + 0, 2);
|
||||
cblas_scopy((int)countToDo, right_ptr, 1, outBuffer + 1, 2);
|
||||
|
||||
inBuffer += countToDo * channelCount;
|
||||
outBuffer += countToDo * 2;
|
||||
|
||||
count -= countToDo;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
for(int i = 0; i < channelCount; ++i) {
|
||||
vDSP_vclr(prevInputs[i], 1, fftSize);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -42,6 +42,8 @@ using std::atomic_long;
|
|||
|
||||
BOOL rsDone;
|
||||
void *rsstate, *rsold;
|
||||
|
||||
double lastClippedSampleRate;
|
||||
|
||||
void *rsvis;
|
||||
double lastVisRate;
|
||||
|
|
|
@ -85,6 +85,10 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA
|
|||
format.mSampleRate = maxSampleRate;
|
||||
dstRate = maxSampleRate;
|
||||
formatClipped = YES;
|
||||
if(srcRate != lastClippedSampleRate) {
|
||||
lastClippedSampleRate = srcRate;
|
||||
streamFormatStarted = NO;
|
||||
}
|
||||
}
|
||||
if(!streamFormatStarted || config != realStreamChannelConfig || memcmp(&newFormat, &format, sizeof(format)) != 0) {
|
||||
[currentPtsLock lock];
|
||||
|
@ -165,7 +169,7 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA
|
|||
[currentPtsLock lock];
|
||||
samplesFlushed = (int)rsstate_flush(rsvis, &visTemp[0], 8192);
|
||||
[currentPtsLock unlock];
|
||||
if(samplesFlushed) {
|
||||
if(samplesFlushed > 1) {
|
||||
[visController postVisPCM:visTemp amount:samplesFlushed];
|
||||
} else {
|
||||
break;
|
||||
|
@ -203,7 +207,7 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA
|
|||
[currentPtsLock lock];
|
||||
samplesFlushed = (int)rsstate_flush(rsvis, &visTemp[0], 8192);
|
||||
[currentPtsLock unlock];
|
||||
if(samplesFlushed) {
|
||||
if(samplesFlushed > 1) {
|
||||
[visController postVisPCM:visTemp amount:samplesFlushed];
|
||||
} else {
|
||||
break;
|
||||
|
@ -802,7 +806,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
[currentPtsLock lock];
|
||||
samplesRendered = rsstate_flush(rsold, &rsTempBuffer[0], 4096);
|
||||
[currentPtsLock unlock];
|
||||
if(!samplesRendered) {
|
||||
if(samplesRendered < 4096) {
|
||||
rsstate_delete(rsold);
|
||||
rsold = NULL;
|
||||
rsDone = YES;
|
||||
|
@ -817,7 +821,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
rsDone = NO;
|
||||
realStreamFormat = newFormat;
|
||||
realStreamChannelConfig = newChannelConfig;
|
||||
streamFormatChanged = YES;
|
||||
[self updateStreamFormat];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -962,6 +966,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
rsDone = NO;
|
||||
rsstate = NULL;
|
||||
rsold = NULL;
|
||||
|
||||
lastClippedSampleRate = 0.0;
|
||||
|
||||
rsvis = NULL;
|
||||
lastVisRate = 44100.0;
|
||||
|
|
137
Audio/Output/OutputCoreAudio.h
Normal file
137
Audio/Output/OutputCoreAudio.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// OutputCoreAudio.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 7/25/23.
|
||||
// Copyright 2023-2024 Christopher Snowhill. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AssertMacros.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <CoreAudio/CoreAudioTypes.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#import <atomic>
|
||||
using std::atomic_long;
|
||||
#else
|
||||
#import <stdatomic.h>
|
||||
#endif
|
||||
|
||||
#import <simd/simd.h>
|
||||
|
||||
#import <CogAudio/ChunkList.h>
|
||||
|
||||
#import <CogAudio/Node.h>
|
||||
|
||||
#import <CogAudio/DSPDownmixNode.h>
|
||||
#import <CogAudio/DSPHRTFNode.h>
|
||||
|
||||
//#define OUTPUT_LOG
|
||||
|
||||
@class OutputNode;
|
||||
|
||||
@class AudioChunk;
|
||||
|
||||
@interface OutputCoreAudio : Node {
|
||||
OutputNode *outputController;
|
||||
|
||||
NSLock *outputLock;
|
||||
|
||||
double streamTimestamp;
|
||||
|
||||
BOOL stopInvoked;
|
||||
BOOL stopCompleted;
|
||||
BOOL running;
|
||||
BOOL stopping;
|
||||
BOOL stopped;
|
||||
BOOL started;
|
||||
BOOL paused;
|
||||
BOOL restarted;
|
||||
BOOL commandStop;
|
||||
BOOL resetting;
|
||||
|
||||
BOOL cutOffInput;
|
||||
BOOL fading, faded;
|
||||
float fadeLevel;
|
||||
float fadeStep;
|
||||
float fadeTarget;
|
||||
|
||||
BOOL eqEnabled;
|
||||
BOOL eqInitialized;
|
||||
|
||||
BOOL streamFormatStarted;
|
||||
BOOL streamFormatChanged;
|
||||
|
||||
double secondsHdcdSustained;
|
||||
|
||||
BOOL defaultdevicelistenerapplied;
|
||||
BOOL currentdevicelistenerapplied;
|
||||
BOOL devicealivelistenerapplied;
|
||||
BOOL observersapplied;
|
||||
BOOL outputdevicechanged;
|
||||
|
||||
float volume;
|
||||
float eqPreamp;
|
||||
|
||||
AVAudioFormat *_deviceFormat;
|
||||
|
||||
AudioDeviceID outputDeviceID;
|
||||
AudioStreamBasicDescription deviceFormat;
|
||||
AudioStreamBasicDescription realStreamFormat; // stream format pre-hrtf
|
||||
AudioStreamBasicDescription streamFormat; // stream format last seen in render callback
|
||||
|
||||
uint32_t deviceChannelConfig;
|
||||
uint32_t realStreamChannelConfig;
|
||||
uint32_t streamChannelConfig;
|
||||
|
||||
AUAudioUnit *_au;
|
||||
|
||||
size_t _bufferSize;
|
||||
|
||||
BOOL resetStreamFormat;
|
||||
|
||||
BOOL shouldPlayOutBuffer;
|
||||
|
||||
BOOL DSPsLaunched;
|
||||
DSPHRTFNode *hrtfNode;
|
||||
DSPDownmixNode *downmixNode;
|
||||
|
||||
#ifdef OUTPUT_LOG
|
||||
NSFileHandle *_logFile;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (id)initWithController:(OutputNode *)c;
|
||||
|
||||
- (BOOL)setup;
|
||||
- (OSStatus)setOutputDeviceByID:(int)deviceID;
|
||||
- (BOOL)setOutputDeviceWithDeviceDict:(NSDictionary *)deviceDict;
|
||||
- (void)start;
|
||||
- (void)pause;
|
||||
- (void)resume;
|
||||
- (void)stop;
|
||||
|
||||
- (void)fadeOut;
|
||||
- (void)fadeOutBackground;
|
||||
- (void)fadeIn;
|
||||
|
||||
- (double)latency;
|
||||
|
||||
- (double)volume;
|
||||
- (void)setVolume:(double)v;
|
||||
|
||||
- (void)setShouldPlayOutBuffer:(BOOL)enabled;
|
||||
|
||||
- (void)sustainHDCD;
|
||||
|
||||
- (AudioStreamBasicDescription)deviceFormat;
|
||||
- (uint32_t)deviceChannelConfig;
|
||||
|
||||
- (DSPDownmixNode *)downmix;
|
||||
|
||||
@end
|
1053
Audio/Output/OutputCoreAudio.m
Normal file
1053
Audio/Output/OutputCoreAudio.m
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,10 @@
|
|||
// Plugins! HOORAY!
|
||||
|
||||
#import "AudioChunk.h"
|
||||
#if __has_include(<CogAudio/AudioChunk.h>)
|
||||
# import <CogAudio/AudioChunk.h>
|
||||
#else
|
||||
# import "AudioChunk.h"
|
||||
#endif
|
||||
|
||||
@protocol CogSource <NSObject>
|
||||
+ (NSArray *)schemes; // http, file, etc
|
||||
|
@ -106,9 +110,10 @@
|
|||
- (int)putMetadataInURL:(NSURL *)url;
|
||||
@end
|
||||
|
||||
static NSString *guess_encoding_of_string(const char *input) {
|
||||
NSString *ret = @"";
|
||||
NSData *stringData = [NSData dataWithBytes:input length:strlen(input)];
|
||||
[NSString stringEncodingForData:stringData encodingOptions:nil convertedString:&ret usedLossyConversion:nil];
|
||||
return ret;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern NSString *guess_encoding_of_string(const char *input);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Plugin.h"
|
||||
#import <CogAudio/Plugin.h>
|
||||
|
||||
// Singletonish
|
||||
@interface PluginController : NSObject <CogPluginController> {
|
||||
|
|
|
@ -31,6 +31,7 @@ static std::map<std::string, Cached_Metadata> Cache_List;
|
|||
static RedundantPlaylistDataStore *Cache_Data_Store = nil;
|
||||
|
||||
static bool Cache_Running = false;
|
||||
static bool Cache_Stopped = false;
|
||||
|
||||
static std::thread *Cache_Thread = NULL;
|
||||
|
||||
|
@ -44,6 +45,8 @@ static void cache_init() {
|
|||
static void cache_deinit() {
|
||||
Cache_Running = false;
|
||||
Cache_Thread->join();
|
||||
while(!Cache_Stopped)
|
||||
usleep(500);
|
||||
delete Cache_Thread;
|
||||
Cache_Data_Store = nil;
|
||||
}
|
||||
|
@ -135,6 +138,8 @@ static void cache_run() {
|
|||
|
||||
std::this_thread::sleep_for(dura);
|
||||
}
|
||||
|
||||
Cache_Stopped = true;
|
||||
}
|
||||
|
||||
@implementation PluginController
|
||||
|
@ -384,8 +389,6 @@ static NSString *xmlEscapeString(NSString * string) {
|
|||
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n\
|
||||
<plist version=\"1.0\">\n\
|
||||
<dict>\n\
|
||||
\t<key>FirebaseCrashlyticsCollectionEnabled</key>\n\
|
||||
\t<false/>\n\
|
||||
\t<key>SUEnableInstallerLauncherService</key>\n\
|
||||
\t<true/>\n\
|
||||
\t<key>CFBundleDevelopmentRegion</key>\n\
|
||||
|
@ -464,12 +467,22 @@ static NSString *xmlEscapeString(NSString * string) {
|
|||
\t<string>MediaKeysApplication</string>\n\
|
||||
\t<key>NSRemindersUsageDescription</key>\n\
|
||||
\t<string>Cog has no use for your reminders. Why are you trying to access them with an audio player?</string>\n\
|
||||
\t<key>NSDownloadsFolderUsageDescription</key>\n\
|
||||
\t<string>We may request related audio files from this folder for playback purposes. We will only play back files you specifically add, unless you enable the option to add an entire folder. Granting permission either for individual files or for parent folders ensures their contents will remain playable in future sessions.</string>\n\
|
||||
\t<key>NSDocumentsFolderUsageDescription</key>\n\
|
||||
\t<string>We may request related audio files from this folder for playback purposes. We will only play back files you specifically add, unless you enable the option to add an entire folder. Granting permission either for individual files or for parent folders ensures their contents will remain playable in future sessions.</string>\n\
|
||||
\t<key>NSDesktopFolderUsageDescription</key>\n\
|
||||
\t<string>We may request related audio files from this folder for playback purposes. We will only play back files you specifically add, unless you enable the option to add an entire folder. Granting permission either for individual files or for parent folders ensures their contents will remain playable in future sessions.</string>\n\
|
||||
\t<key>NSMotionUsageDescription</key>\n\
|
||||
\t<string>Cog optionally supports motion tracking headphones for head tracked positional audio, using its own low latency positioning model.</string>\n\
|
||||
\t<key>OSAScriptingDefinition</key>\n\
|
||||
\t<string>Cog.sdef</string>\n\
|
||||
\t<key>SUFeedURL</key>\n\
|
||||
\t<string>https://cogcdn.cog.losno.co/mercury.xml</string>\n\
|
||||
\t<key>SUPublicEDKey</key>\n\
|
||||
\t<string>omxG7Rp0XK9/YEvKbVy7cd44eVAh1LJB6CmjQwjOJz4=</string>\n\
|
||||
\t<key>ITSAppUsesNonExemptEncryption</key>\n\
|
||||
\t<false/>\n\
|
||||
</dict>\n\
|
||||
</plist>\n";
|
||||
NSMutableArray * decodersRegistered = [[NSMutableArray alloc] init];
|
||||
|
@ -817,3 +830,24 @@ static NSString *xmlEscapeString(NSString * string) {
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
NSString *guess_encoding_of_string(const char *input) {
|
||||
NSString *ret = @"";
|
||||
if(input && *input) {
|
||||
@try {
|
||||
ret = [NSString stringWithUTF8String:input];
|
||||
}
|
||||
@catch(NSException *e) {
|
||||
ret = nil;
|
||||
}
|
||||
if(!ret) {
|
||||
// This method is incredibly slow
|
||||
NSData *stringData = [NSData dataWithBytes:input length:strlen(input)];
|
||||
[NSString stringEncodingForData:stringData encodingOptions:nil convertedString:&ret usedLossyConversion:nil];
|
||||
if(!ret) {
|
||||
ret = @"";
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
137
Audio/ThirdParty/deadbeef/fft_accelerate.c
vendored
137
Audio/ThirdParty/deadbeef/fft_accelerate.c
vendored
|
@ -24,17 +24,28 @@
|
|||
#include "fft.h"
|
||||
#include <Accelerate/Accelerate.h>
|
||||
|
||||
static int _fft_size;
|
||||
static float *_input_real;
|
||||
static float *_input_imaginary;
|
||||
static float *_output_real;
|
||||
static float *_output_imaginary;
|
||||
static float *_hamming;
|
||||
static float *_sq_mags;
|
||||
// Some newer spectrum calculation methodology, adapted but not copied wholesale
|
||||
// Mostly about a dozen or two lines of Cocoa and vDSP code
|
||||
|
||||
static vDSP_DFT_Setup _dft_setup;
|
||||
// AudioSpectrum: A sample app using Audio Unit and vDSP
|
||||
// By Keijiro Takahashi, 2013, 2014
|
||||
// https://github.com/keijiro/AudioSpectrum
|
||||
|
||||
struct SpectrumData
|
||||
{
|
||||
unsigned long length;
|
||||
Float32 data[0];
|
||||
};
|
||||
|
||||
static int _fft_size = 0;
|
||||
static vDSP_DFT_Setup _dftSetup = NULL;
|
||||
static DSPSplitComplex _dftBuffer = {0};
|
||||
static Float32 *_window = NULL;
|
||||
|
||||
static struct SpectrumData *_rawSpectrum = NULL;
|
||||
|
||||
// Apparently _mm_malloc is Intel-only on newer macOS targets, so use supported posix_memalign
|
||||
// malloc() is allegedly aligned on macOS, but I don't know for sure
|
||||
static void *_memalign_calloc(size_t count, size_t size, size_t align) {
|
||||
size *= count;
|
||||
void *ret = NULL;
|
||||
|
@ -50,57 +61,79 @@ _init_buffers(int fft_size) {
|
|||
if(fft_size != _fft_size) {
|
||||
fft_free();
|
||||
|
||||
_input_real = _memalign_calloc(fft_size * 2, sizeof(float), 16);
|
||||
_input_imaginary = _memalign_calloc(fft_size * 2, sizeof(float), 16);
|
||||
_hamming = _memalign_calloc(fft_size * 2, sizeof(float), 16);
|
||||
_sq_mags = _memalign_calloc(fft_size, sizeof(float), 16);
|
||||
_output_real = _memalign_calloc(fft_size * 2 + 1, sizeof(float), 16);
|
||||
_output_imaginary = _memalign_calloc(fft_size * 2 + 1, sizeof(float), 16);
|
||||
_dftSetup = vDSP_DFT_zrop_CreateSetup(NULL, fft_size * 2, vDSP_DFT_FORWARD);
|
||||
if(!_dftSetup) return;
|
||||
|
||||
_dft_setup = vDSP_DFT_zop_CreateSetup(NULL, fft_size * 2, FFT_FORWARD);
|
||||
vDSP_hamm_window(_hamming, fft_size * 2, 0);
|
||||
_dftBuffer.realp = _memalign_calloc(fft_size, sizeof(Float32), 16);
|
||||
_dftBuffer.imagp = _memalign_calloc(fft_size, sizeof(Float32), 16);
|
||||
if(!_dftBuffer.realp || !_dftBuffer.imagp) return;
|
||||
|
||||
_window = _memalign_calloc(fft_size * 2, sizeof(Float32), 16);
|
||||
if(!_window) return;
|
||||
vDSP_blkman_window(_window, fft_size * 2, 0);
|
||||
|
||||
Float32 normFactor = 2.0f / (fft_size * 2);
|
||||
vDSP_vsmul(_window, 1, &normFactor, _window, 1, fft_size * 2);
|
||||
|
||||
_rawSpectrum = (struct SpectrumData *) _memalign_calloc(sizeof(struct SpectrumData) + sizeof(Float32) * fft_size, 1, 16);
|
||||
if(!_rawSpectrum) return;
|
||||
_rawSpectrum->length = fft_size;
|
||||
|
||||
_fft_size = fft_size;
|
||||
}
|
||||
}
|
||||
|
||||
void fft_calculate(const float *data, float *freq, int fft_size) {
|
||||
int dft_size = fft_size * 2;
|
||||
|
||||
if(!freq || !fft_size) return;
|
||||
_init_buffers(fft_size);
|
||||
|
||||
vDSP_vmul(data, 1, _hamming, 1, _input_real, 1, dft_size);
|
||||
|
||||
vDSP_DFT_Execute(_dft_setup, _input_real, _input_imaginary, _output_real, _output_imaginary);
|
||||
|
||||
DSPSplitComplex split_complex = {
|
||||
.realp = _output_real,
|
||||
.imagp = _output_imaginary
|
||||
};
|
||||
vDSP_zvmags(&split_complex, 1, _sq_mags, 1, fft_size);
|
||||
|
||||
int sq_count = fft_size;
|
||||
vvsqrtf(_sq_mags, _sq_mags, &sq_count);
|
||||
|
||||
float mult = 2.f / fft_size;
|
||||
vDSP_vsmul(_sq_mags, 1, &mult, freq, 1, fft_size);
|
||||
}
|
||||
|
||||
void fft_free(void) {
|
||||
free(_input_real);
|
||||
free(_input_imaginary);
|
||||
free(_hamming);
|
||||
free(_sq_mags);
|
||||
free(_output_real);
|
||||
free(_output_imaginary);
|
||||
if(_dft_setup != NULL) {
|
||||
vDSP_DFT_DestroySetup(_dft_setup);
|
||||
if(!_fft_size || !data) {
|
||||
// Decibels
|
||||
float kZeroLevel = -128.0;
|
||||
vDSP_vfill(&kZeroLevel, freq, 1, fft_size);
|
||||
return;
|
||||
}
|
||||
_input_real = NULL;
|
||||
_input_imaginary = NULL;
|
||||
_hamming = NULL;
|
||||
_sq_mags = NULL;
|
||||
_dft_setup = NULL;
|
||||
_output_real = NULL;
|
||||
_output_imaginary = NULL;
|
||||
|
||||
// Split the waveform
|
||||
DSPSplitComplex dest = { _dftBuffer.realp, _dftBuffer.imagp };
|
||||
vDSP_ctoz((const DSPComplex*)data, 2, &dest, 1, fft_size);
|
||||
|
||||
// Apply the window function
|
||||
vDSP_vmul(_dftBuffer.realp, 1, _window, 2, _dftBuffer.realp, 1, fft_size);
|
||||
vDSP_vmul(_dftBuffer.imagp, 1, _window + 1, 2, _dftBuffer.imagp, 1, fft_size);
|
||||
|
||||
// DFT
|
||||
vDSP_DFT_Execute(_dftSetup, _dftBuffer.realp, _dftBuffer.imagp, _dftBuffer.realp, _dftBuffer.imagp);
|
||||
|
||||
// Zero out the Nyquist value
|
||||
_dftBuffer.imagp[0] = 0.0;
|
||||
|
||||
// Calculate power spectrum
|
||||
Float32 *rawSpectrum = _rawSpectrum->data;
|
||||
vDSP_zvmags(&_dftBuffer, 1, rawSpectrum, 1, fft_size);
|
||||
|
||||
// Add -128dB offset to avoid log(0)
|
||||
float kZeroOffset = 1.5849e-13;
|
||||
vDSP_vsadd(rawSpectrum, 1, &kZeroOffset, rawSpectrum, 1, fft_size);
|
||||
|
||||
// Convert power to decibel
|
||||
float kZeroDB = 0.70710678118f; // 1/sqrt(2)
|
||||
vDSP_vdbcon(rawSpectrum, 1, &kZeroDB, rawSpectrum, 1, fft_size, 0);
|
||||
|
||||
cblas_scopy(fft_size, rawSpectrum, 1, freq, 1);
|
||||
}
|
||||
|
||||
void __attribute__((destructor)) fft_free(void) {
|
||||
free(_dftBuffer.realp);
|
||||
free(_dftBuffer.imagp);
|
||||
free(_window);
|
||||
free(_rawSpectrum);
|
||||
if(_dftSetup != NULL) {
|
||||
vDSP_DFT_DestroySetup(_dftSetup);
|
||||
}
|
||||
_dftBuffer.realp = NULL;
|
||||
_dftBuffer.imagp = NULL;
|
||||
_window = NULL;
|
||||
_rawSpectrum = NULL;
|
||||
_dftSetup = NULL;
|
||||
_fft_size = 0;
|
||||
}
|
||||
|
|
4
Audio/ThirdParty/hdcd/hdcd_decode2.c
vendored
4
Audio/ThirdParty/hdcd/hdcd_decode2.c
vendored
|
@ -1044,8 +1044,8 @@ static int hdcd_envelope(int32_t *samples, int count, int stride, int gain, int
|
|||
|
||||
/* hold a steady level */
|
||||
if (gain == 0x800000) {
|
||||
if (count > 0)
|
||||
samples += count * stride;
|
||||
/*if (count > 0)
|
||||
samples += count * stride;*/
|
||||
} else {
|
||||
while (--count >= 0) {
|
||||
APPLY_GAIN(*samples, gain);
|
||||
|
|
2
Audio/ThirdParty/hrtf/HrtfData.cpp
vendored
2
Audio/ThirdParty/hrtf/HrtfData.cpp
vendored
|
@ -9,7 +9,7 @@ typedef struct {
|
|||
uint8_t bytes[3];
|
||||
} sample_int24_t;
|
||||
|
||||
const double pi = 3.1415926535897932385;
|
||||
const double pi = M_PI;
|
||||
|
||||
template <typename T>
|
||||
void read_stream(std::istream& stream, T& value) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
39
Audio/ThirdParty/lvqcl/License/License.txt
vendored
Normal file
39
Audio/ThirdParty/lvqcl/License/License.txt
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
SoX resampler plugin for foobar2000 audio player
|
||||
Copyright (C) lvqcl
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
This software uses code of SoX licensed under the terms of LGPLv2.1
|
||||
Copyright (C) robs@users.sourceforge.net
|
||||
Copyright (C) Chris Bagwell and SoX contributors
|
||||
Copyright (C) Reuben Thomas
|
||||
|
||||
|
||||
This software uses code of FFmpeg licensed under the terms of LGPLv2.1
|
||||
Copyright (C) Fabrice Bellard
|
||||
Copyright (C) Michael Niedermayer <michaelni@gmx.at>
|
||||
Copyright (C) Alex Converse <alex dot converse at gmail dot com>
|
||||
Copyright (C) Loren Merritt
|
||||
Copyright (C) Vitor Sessak
|
||||
Copyright (C) x264 project
|
||||
|
||||
|
||||
This software uses code of General Purpose FFT Package
|
||||
Copyright (C) Takuya OOURA
|
||||
|
||||
|
||||
This software uses code of foobar2000 1.4 SDK
|
||||
Copyright (C) 2001-2018, Peter Pawlowski
|
220
Audio/ThirdParty/lvqcl/lpc.c
vendored
Normal file
220
Audio/ThirdParty/lvqcl/lpc.c
vendored
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2018 lvqcl
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "lpc.h"
|
||||
|
||||
static void apply_window(float *const data, const size_t data_len) {
|
||||
#if 0
|
||||
if (0) // subtract the mean
|
||||
{
|
||||
double mean = 0;
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
mean += data[i];
|
||||
mean /= data_len;
|
||||
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
data[i] -= (float)mean;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(1) // Welch window
|
||||
{
|
||||
const float n2 = (data_len + 1) / 2.0f;
|
||||
for(int i = 0; i < (int)data_len; i++) {
|
||||
float k = (i + 1 - n2) / n2;
|
||||
data[data_len - 1 - i] *= 1.0f - k * k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float vorbis_lpc_from_data(float *data, float *lpci, int n, int m, double *aut, double *lpc) {
|
||||
double error;
|
||||
double epsilon;
|
||||
int i, j;
|
||||
|
||||
/* autocorrelation, p+1 lag coefficients */
|
||||
j = m + 1;
|
||||
while(j--) {
|
||||
double d = 0; /* double needed for accumulator depth */
|
||||
for(i = j; i < n; i++) d += (double)data[i] * data[i - j];
|
||||
aut[j] = d;
|
||||
}
|
||||
|
||||
/* Generate lpc coefficients from autocorr values */
|
||||
|
||||
/* set our noise floor to about -100dB */
|
||||
error = aut[0] * (1. + 1e-10);
|
||||
epsilon = 1e-9 * aut[0] + 1e-10;
|
||||
|
||||
for(i = 0; i < m; i++) {
|
||||
double r = -aut[i + 1];
|
||||
|
||||
if(error < epsilon) {
|
||||
memset(lpc + i, 0, (m - i) * sizeof(*lpc));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Sum up this iteration's reflection coefficient; note that in
|
||||
Vorbis we don't save it. If anyone wants to recycle this code
|
||||
and needs reflection coefficients, save the results of 'r' from
|
||||
each iteration. */
|
||||
|
||||
for(j = 0; j < i; j++) r -= lpc[j] * aut[i - j];
|
||||
r /= error;
|
||||
|
||||
/* Update LPC coefficients and total error */
|
||||
|
||||
lpc[i] = r;
|
||||
for(j = 0; j < i / 2; j++) {
|
||||
double tmp = lpc[j];
|
||||
|
||||
lpc[j] += r * lpc[i - 1 - j];
|
||||
lpc[i - 1 - j] += r * tmp;
|
||||
}
|
||||
if(i & 1) lpc[j] += lpc[j] * r;
|
||||
|
||||
error *= 1. - r * r;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
/* slightly damp the filter */
|
||||
{
|
||||
double g = .99;
|
||||
double damp = g;
|
||||
for(j = 0; j < m; j++) {
|
||||
lpc[j] *= damp;
|
||||
damp *= g;
|
||||
}
|
||||
}
|
||||
|
||||
for(j = 0; j < m; j++) lpci[j] = (float)lpc[j];
|
||||
|
||||
/* we need the error value to know how big an impulse to hit the
|
||||
filter with later */
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void vorbis_lpc_predict(float *coeff, float *prime, int m, float *data, long n, float *work) {
|
||||
/* in: coeff[0...m-1] LPC coefficients
|
||||
prime[0...m-1] initial values (allocated size of n+m-1)
|
||||
out: data[0...n-1] data samples */
|
||||
|
||||
long i, j, o, p;
|
||||
float y;
|
||||
|
||||
if(!prime)
|
||||
for(i = 0; i < m; i++)
|
||||
work[i] = 0.f;
|
||||
else
|
||||
for(i = 0; i < m; i++)
|
||||
work[i] = prime[i];
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
y = 0;
|
||||
o = i;
|
||||
p = m;
|
||||
for(j = 0; j < m; j++)
|
||||
y -= work[o++] * coeff[--p];
|
||||
|
||||
data[i] = work[o] = y;
|
||||
}
|
||||
}
|
||||
|
||||
void lpc_extrapolate2(float *const data, const size_t data_len, const int nch, const int lpc_order, const size_t extra_bkwd, const size_t extra_fwd, void **extrapolate_buffer, size_t *extrapolate_buffer_size) {
|
||||
//const size_t max_to_prime = (data_len < lpc_order) ? data_len : lpc_order;
|
||||
const size_t min_data_len = (data_len < lpc_order) ? lpc_order : data_len;
|
||||
|
||||
const size_t tdata_size = sizeof(float) * (extra_bkwd + min_data_len + extra_fwd);
|
||||
const size_t aut_size = sizeof(double) * (lpc_order + 1);
|
||||
const size_t lpc_size = sizeof(double) * lpc_order;
|
||||
const size_t lpci_size = sizeof(float) * lpc_order;
|
||||
const size_t work_size = sizeof(float) * (extra_bkwd + lpc_order + extra_fwd);
|
||||
|
||||
const size_t new_size = tdata_size + aut_size + lpc_size + lpci_size + work_size;
|
||||
|
||||
if(new_size > *extrapolate_buffer_size) {
|
||||
*extrapolate_buffer = realloc(*extrapolate_buffer, new_size);
|
||||
*extrapolate_buffer_size = new_size;
|
||||
if(!*extrapolate_buffer) return;
|
||||
}
|
||||
|
||||
double *aut = (double *)(*extrapolate_buffer);
|
||||
double *lpc = (double *)(*extrapolate_buffer + aut_size);
|
||||
|
||||
float *tdata = (float *)(*extrapolate_buffer + aut_size + lpc_size); // for 1 channel only
|
||||
|
||||
float *lpci = (float *)(*extrapolate_buffer + aut_size + lpc_size + tdata_size);
|
||||
float *work = (float *)(*extrapolate_buffer + aut_size + lpc_size + tdata_size + lpci_size);
|
||||
|
||||
for(int c = 0; c < nch; c++) {
|
||||
if(extra_bkwd) {
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
tdata[min_data_len - 1 - i] = data[i * nch + c];
|
||||
if(data_len < min_data_len)
|
||||
for(int i = (int)data_len; i < (int)min_data_len; i++)
|
||||
tdata[min_data_len - 1 - i] = 0.0f;
|
||||
} else {
|
||||
const ssize_t len_diff = min_data_len - data_len;
|
||||
if(len_diff <= 0) {
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
tdata[i] = data[i * nch + c];
|
||||
} else {
|
||||
for(int i = 0; i < (int)len_diff; i++)
|
||||
tdata[i] = 0.0f;
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
tdata[len_diff + i] = data[i * nch + c];
|
||||
}
|
||||
}
|
||||
|
||||
apply_window(tdata, min_data_len);
|
||||
vorbis_lpc_from_data(tdata, lpci, (int)min_data_len, lpc_order, aut, lpc);
|
||||
|
||||
// restore after apply_window
|
||||
if(extra_bkwd) {
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
tdata[min_data_len - 1 - i] = data[i * nch + c];
|
||||
if(data_len < min_data_len)
|
||||
for(int i = (int)data_len; i < (int)min_data_len; i++)
|
||||
tdata[min_data_len - 1 - i] = 0.0f;
|
||||
} else {
|
||||
const ssize_t len_diff = min_data_len - data_len;
|
||||
if(len_diff <= 0) {
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
tdata[i] = data[i * nch + c];
|
||||
} else {
|
||||
for(int i = 0; i < (int)len_diff; i++)
|
||||
tdata[i] = 0.0f;
|
||||
for(int i = 0; i < (int)data_len; i++)
|
||||
tdata[len_diff + i] = data[i * nch + c];
|
||||
}
|
||||
}
|
||||
|
||||
vorbis_lpc_predict(lpci, tdata + min_data_len - lpc_order, lpc_order, tdata + min_data_len, extra_fwd + extra_bkwd, work);
|
||||
|
||||
if(extra_bkwd) {
|
||||
for(int i = 0; i < extra_bkwd; i++)
|
||||
data[(-i - 1) * nch + c] = tdata[min_data_len + i];
|
||||
} else {
|
||||
for(int i = 0; i < extra_fwd; i++)
|
||||
data[(i + data_len) * nch + c] = tdata[min_data_len + i];
|
||||
}
|
||||
}
|
||||
}
|
40
Audio/ThirdParty/lvqcl/lpc.h
vendored
Normal file
40
Audio/ThirdParty/lvqcl/lpc.h
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef MY_LPC_H
|
||||
#define MY_LPC_H
|
||||
|
||||
/* data - beginning of the data
|
||||
* data_len - length of data (in samples) that are base for extrapolation
|
||||
* nch - number of (interleaved) channels
|
||||
* lpc_order - LPC order
|
||||
* extra_bkwd - number of samples to pre-extrapolate
|
||||
* extra_fwd - number of samples to post-extrapolate
|
||||
*
|
||||
* D = data; N = num_channels; LEN = data_len*N; EX = extra*N
|
||||
*
|
||||
* memory layout when invdir == false:
|
||||
*
|
||||
* [||||||||||||||||||||||||||||||||][||||||||||||||||||||][
|
||||
* ^ D[0] ^ D[LEN] ^ D[LEN+EX]
|
||||
*
|
||||
* memory layout when invdir == true:
|
||||
* ][||||||||||||||||||||][||||||||||||||||||||||||||||||||][
|
||||
* ^ D[0] ^ D[LEN]
|
||||
* ^ D[-1*N-EX] ^ D[-1*N]
|
||||
*
|
||||
*/
|
||||
|
||||
static const size_t LPC_ORDER = 32;
|
||||
|
||||
void lpc_extrapolate2(float * const data, const size_t data_len, const int nch, const int lpc_order, const size_t extra_bkwd, const size_t extra_fwd, void ** extrapolate_buffer, size_t * extrapolate_buffer_size);
|
||||
|
||||
static inline void lpc_extrapolate_bkwd(float * const data, const size_t data_len, const size_t prime_len, const int nch, const int lpc_order, const size_t extra_bkwd, void ** extrapolate_buffer, size_t * extrapolate_buffer_size)
|
||||
{
|
||||
(void)data_len;
|
||||
lpc_extrapolate2(data, prime_len, nch, lpc_order, extra_bkwd, 0, extrapolate_buffer, extrapolate_buffer_size);
|
||||
}
|
||||
|
||||
static inline void lpc_extrapolate_fwd(float * const data, const size_t data_len, const size_t prime_len, const int nch, const int lpc_order, const size_t extra_fwd, void ** extrapolate_buffer, size_t * extrapolate_buffer_size)
|
||||
{
|
||||
lpc_extrapolate2(data + (data_len - prime_len)*nch, prime_len, nch, lpc_order, 0, extra_fwd, extrapolate_buffer, extrapolate_buffer_size);
|
||||
}
|
||||
|
||||
#endif
|
56
Audio/ThirdParty/lvqcl/util.h
vendored
Normal file
56
Audio/ThirdParty/lvqcl/util.h
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* Copyright (c) 2018 lvqcl. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H_
|
||||
#define UTIL_H_
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) (((a)<(b))?(a):(b))
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(a,b) (((a)>(b))?(a):(b))
|
||||
#endif
|
||||
|
||||
static inline unsigned local_gcd(unsigned a, unsigned b)
|
||||
{
|
||||
if (a == 0 || b == 0) return 0;
|
||||
unsigned c = a % b;
|
||||
while (c != 0) { a = b; b = c; c = a % b; }
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
In: *r1 and *r2: samplerates;
|
||||
Out: *r1 and *r2: numbers of samples;
|
||||
|
||||
multiply r1 and r2 by n so that durations is 1/N th of second;
|
||||
limit n so that r1 and r2 aren't bigger than M
|
||||
*/
|
||||
static void samples_len(unsigned* r1, unsigned* r2, unsigned N, unsigned M) // example: r1 = 44100, r2 = 48000, N = 20, M = 8192
|
||||
{
|
||||
if (r1 == 0 || r2 == 0) return;
|
||||
unsigned v = local_gcd(*r1, *r2); // v = 300
|
||||
if (v == 0) return;
|
||||
*r1 /= v; *r2 /= v; // r1 = 147; r2 = 160 == 1/300th of second
|
||||
unsigned n = (v + N-1) / N; // n = 300/20 = 15 times
|
||||
unsigned z = max(*r1, *r2); // z = 160
|
||||
if (z*n > M) n = M / z; // 160*15 = 2400 < 8192;; if M == 1024: n = 1024/160 = 6; 160*6 = 960
|
||||
if (n < 1) n = 1;
|
||||
*r1 *= n; *r2 *= n; // r1 = 147*15 = 2205 samples, r2 = 160*15 = 2400 samples
|
||||
}
|
||||
|
||||
#endif
|
1
Audio/ThirdParty/rsstate.hpp
vendored
1
Audio/ThirdParty/rsstate.hpp
vendored
|
@ -74,6 +74,7 @@ struct rsstate {
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
return (int)outTotal;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -10,21 +10,20 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface VisualizationController : NSObject {
|
||||
double sampleRate;
|
||||
double latency;
|
||||
float *visAudio;
|
||||
int visAudioCursor, visAudioSize;
|
||||
float *visAudioTemp;
|
||||
}
|
||||
|
||||
+ (VisualizationController *)sharedController;
|
||||
|
||||
- (void)postLatency:(double)latency;
|
||||
|
||||
- (UInt64)samplesPosted;
|
||||
|
||||
- (void)postSampleRate:(double)sampleRate;
|
||||
- (void)postVisPCM:(const float *)inPCM amount:(int)amount;
|
||||
- (double)readSampleRate;
|
||||
- (void)copyVisPCM:(float *)outPCM visFFT:(float *)outFFT latencyOffset:(double)latency;
|
||||
- (void)copyVisPCM:(float *_Nullable)outPCM visFFT:(float *_Nullable)outFFT latencyOffset:(double)latency;
|
||||
|
||||
- (void)reset;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -10,7 +10,15 @@
|
|||
|
||||
#import "fft.h"
|
||||
|
||||
@implementation VisualizationController
|
||||
@implementation VisualizationController {
|
||||
double sampleRate;
|
||||
double latency;
|
||||
float *visAudio;
|
||||
int visAudioCursor, visAudioSize;
|
||||
uint64_t visSamplesPosted;
|
||||
BOOL ignoreLatency;
|
||||
double sinePhase;
|
||||
}
|
||||
|
||||
static VisualizationController *_sharedController = nil;
|
||||
|
||||
|
@ -27,11 +35,10 @@ static VisualizationController *_sharedController = nil;
|
|||
self = [super init];
|
||||
if(self) {
|
||||
visAudio = NULL;
|
||||
visAudioSize = 0;
|
||||
latency = 0;
|
||||
visAudioTemp = (float *) calloc(sizeof(float), 4096);
|
||||
if(!visAudioTemp) {
|
||||
return nil;
|
||||
}
|
||||
ignoreLatency = YES;
|
||||
sinePhase = 0.0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -40,6 +47,19 @@ static VisualizationController *_sharedController = nil;
|
|||
fft_free();
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
@synchronized (self) {
|
||||
latency = 0;
|
||||
visAudioCursor = 0;
|
||||
visSamplesPosted = 0;
|
||||
ignoreLatency = YES;
|
||||
sinePhase = 0.0;
|
||||
if(visAudio && visAudioSize) {
|
||||
bzero(visAudio, sizeof(float) * visAudioSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)postSampleRate:(double)sampleRate {
|
||||
@synchronized(self) {
|
||||
if(self->sampleRate != sampleRate) {
|
||||
|
@ -53,6 +73,12 @@ static VisualizationController *_sharedController = nil;
|
|||
self->visAudio = visAudio;
|
||||
self->visAudioSize = visAudioSize;
|
||||
visAudioCursor %= visAudioSize;
|
||||
} else {
|
||||
if(self->visAudio) {
|
||||
free(self->visAudio);
|
||||
self->visAudio = NULL;
|
||||
}
|
||||
self->visAudioSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +86,9 @@ static VisualizationController *_sharedController = nil;
|
|||
|
||||
- (void)postVisPCM:(const float *)inPCM amount:(int)amount {
|
||||
@synchronized(self) {
|
||||
if(!visAudioSize) {
|
||||
return;
|
||||
}
|
||||
int samplesRead = 0;
|
||||
while(amount > 0) {
|
||||
int amountToCopy = (int)(visAudioSize - visAudioCursor);
|
||||
|
@ -69,13 +98,19 @@ static VisualizationController *_sharedController = nil;
|
|||
if(visAudioCursor >= visAudioSize) visAudioCursor -= visAudioSize;
|
||||
amount -= amountToCopy;
|
||||
samplesRead += amountToCopy;
|
||||
visSamplesPosted += amountToCopy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)postLatency:(double)latency {
|
||||
self->latency = latency;
|
||||
assert(latency < 45.0);
|
||||
if((latency >= 45.0) || (latency < 0.0)) [[clang::unlikely]] {
|
||||
ignoreLatency = YES;
|
||||
self->latency = latency;
|
||||
} else {
|
||||
self->latency = latency;
|
||||
ignoreLatency = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (double)readSampleRate {
|
||||
|
@ -84,30 +119,58 @@ static VisualizationController *_sharedController = nil;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)copyVisPCM:(float *)outPCM visFFT:(float *)outFFT latencyOffset:(double)latency {
|
||||
- (UInt64)samplesPosted {
|
||||
return visSamplesPosted;
|
||||
}
|
||||
|
||||
- (void)generateSineWave:(float *_Nullable)outPCM visFFT:(float *_Nullable)outFFT {
|
||||
double sinePhase = self->sinePhase;
|
||||
self->sinePhase = fmod(sinePhase + (M_PI / 90.0), M_PI * 2.0);
|
||||
if(outPCM || outFFT) {
|
||||
const double stepSize = M_PI * 2.0 * 5.0 / 4096.0;
|
||||
double sineStep = sinePhase;
|
||||
for(int i = 0; i < 2048; ++i) {
|
||||
double sinePoint = sin(sineStep);
|
||||
if(outPCM) {
|
||||
outPCM[i * 2] = sinePoint;
|
||||
outPCM[i * 2 + 1] = sin(sineStep + stepSize);
|
||||
}
|
||||
if(outFFT) {
|
||||
outFFT[i] = sinePoint * -40.0 - 40.0;
|
||||
}
|
||||
sineStep = fmod(sineStep + stepSize * 2, M_PI * 2.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)copyVisPCM:(float *_Nullable)outPCM visFFT:(float *_Nullable)outFFT latencyOffset:(double)latency {
|
||||
if(!outPCM && !outFFT) return;
|
||||
|
||||
if(!visAudio || !visAudioSize) {
|
||||
if(outPCM) bzero(outPCM, sizeof(float) * 4096);
|
||||
if(outFFT) bzero(outFFT, sizeof(float) * 2048);
|
||||
if(ignoreLatency || !visAudio || !visAudioSize) {
|
||||
[self generateSineWave:outPCM visFFT:outFFT];
|
||||
return;
|
||||
}
|
||||
|
||||
if(!outPCM) outPCM = &visAudioTemp[0];
|
||||
void *visAudioTemp = calloc(sizeof(float), 4096);
|
||||
if(!visAudioTemp) {
|
||||
[self generateSineWave:outPCM visFFT:outFFT];
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized(self) {
|
||||
if(!sampleRate) {
|
||||
bzero(outPCM, 4096 * sizeof(float));
|
||||
if(outFFT) {
|
||||
bzero(outFFT, 2048 * sizeof(float));
|
||||
}
|
||||
free(visAudioTemp);
|
||||
[self generateSineWave:outPCM visFFT:outFFT];
|
||||
return;
|
||||
}
|
||||
int latencySamples = (int)(sampleRate * (self->latency + latency));
|
||||
int latencySamples = (int)(sampleRate * (self->latency + latency)) + 2048;
|
||||
if(latencySamples < 4096) latencySamples = 4096;
|
||||
int readCursor = visAudioCursor - latencySamples;
|
||||
int samples = 4096;
|
||||
int samplesRead = 0;
|
||||
if(latencySamples + samples > visAudioSize) {
|
||||
samples = (int)(visAudioSize - latencySamples);
|
||||
}
|
||||
while(readCursor < 0)
|
||||
readCursor += visAudioSize;
|
||||
while(readCursor >= visAudioSize)
|
||||
|
@ -115,16 +178,21 @@ static VisualizationController *_sharedController = nil;
|
|||
while(samples > 0) {
|
||||
int samplesToRead = (int)(visAudioSize - readCursor);
|
||||
if(samplesToRead > samples) samplesToRead = samples;
|
||||
cblas_scopy(samplesToRead, visAudio + readCursor, 1, outPCM + samplesRead, 1);
|
||||
cblas_scopy(samplesToRead, visAudio + readCursor, 1, visAudioTemp + samplesRead, 1);
|
||||
samplesRead += samplesToRead;
|
||||
readCursor += samplesToRead;
|
||||
samples -= samplesToRead;
|
||||
if(readCursor >= visAudioSize) readCursor -= visAudioSize;
|
||||
}
|
||||
}
|
||||
if(outFFT) {
|
||||
fft_calculate(outPCM, outFFT, 2048);
|
||||
if(outPCM) {
|
||||
cblas_scopy(4096, visAudioTemp, 1, outPCM, 1);
|
||||
}
|
||||
if(outFFT) {
|
||||
fft_calculate(visAudioTemp, outFFT, 2048);
|
||||
}
|
||||
|
||||
free(visAudioTemp);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,6 +15,7 @@ class VisualizationController : NSObject {
|
|||
var visAudio: [Float] = Array(repeating: 0.0, count: 44100 * 45)
|
||||
var visAudioCursor = 0
|
||||
var visAudioSize = 0
|
||||
var visSamplesPosted: UInt64 = 0
|
||||
|
||||
private static var sharedVisualizationController: VisualizationController = {
|
||||
let visualizationController = VisualizationController()
|
||||
|
@ -25,11 +26,28 @@ class VisualizationController : NSObject {
|
|||
class func sharedController() -> VisualizationController {
|
||||
return sharedVisualizationController
|
||||
}
|
||||
|
||||
@objc
|
||||
func reset() {
|
||||
serialQueue.sync {
|
||||
self.latency = 0;
|
||||
let amount = self.visAudioSize
|
||||
for i in 0..<amount {
|
||||
self.visAudio[i] = 0
|
||||
}
|
||||
self.visSamplesPosted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func postLatency(_ latency: Double) {
|
||||
self.latency = latency
|
||||
}
|
||||
|
||||
@objc
|
||||
func samplesPosted() -> UInt64 {
|
||||
return self.visSamplesPosted
|
||||
}
|
||||
|
||||
@objc
|
||||
func postSampleRate(_ sampleRate: Double) {
|
||||
|
@ -49,12 +67,20 @@ class VisualizationController : NSObject {
|
|||
let bufferPointer = UnsafeBufferPointer<Float>(start: inPCM, count: amount)
|
||||
var j = self.visAudioCursor
|
||||
let k = self.visAudioSize
|
||||
for i in 0..<amount {
|
||||
let x = bufferPointer[i]
|
||||
self.visAudio[j] = x
|
||||
j += 1; if j >= k { j = 0 }
|
||||
if(j + amount <= k) {
|
||||
let endIndex = j + amount;
|
||||
self.visAudio.replaceSubrange(j..<endIndex, with: bufferPointer)
|
||||
j += amount
|
||||
if(j >= k) { j = 0 }
|
||||
} else {
|
||||
let inEndIndex = k - j
|
||||
let remainder = amount - inEndIndex
|
||||
self.visAudio.replaceSubrange(j..<k, with: bufferPointer.prefix(inEndIndex))
|
||||
self.visAudio.replaceSubrange(0..<remainder, with: bufferPointer.suffix(remainder))
|
||||
j = remainder
|
||||
}
|
||||
self.visAudioCursor = j
|
||||
self.visSamplesPosted += UInt64(amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,42 +94,38 @@ class VisualizationController : NSObject {
|
|||
@objc
|
||||
func copyVisPCM(_ outPCM: UnsafeMutablePointer<Float>?, visFFT: UnsafeMutablePointer<Float>?, latencyOffset: Double) {
|
||||
if(self.visAudioSize == 0) {
|
||||
if(outPCM != nil) {
|
||||
let pcmPointer = UnsafeMutableBufferPointer<Float>(start: outPCM, count: 4096)
|
||||
for i in 0...4095 {
|
||||
pcmPointer[i] = 0.0
|
||||
}
|
||||
}
|
||||
if(visFFT != nil) {
|
||||
let fftPointer = UnsafeMutableBufferPointer<Float>(start: visFFT, count: 2048)
|
||||
for i in 0...2047 {
|
||||
fftPointer[i] = 0.0
|
||||
}
|
||||
}
|
||||
outPCM?.update(repeating: 0.0, count: 4096)
|
||||
visFFT?.update(repeating: 0.0, count: 2048)
|
||||
return
|
||||
}
|
||||
|
||||
var outPCMCopy = Array<Float>(repeating: 0.0, count: 4096)
|
||||
|
||||
serialQueue.sync {
|
||||
let latencySamples = (Int)(self.latency * self.sampleRate)
|
||||
// Offset latency so the target sample is in the center of the window
|
||||
let latencySamples = (Int)((self.latency + latencyOffset) * self.sampleRate) + 2048
|
||||
var samplesToDo = 4096;
|
||||
if(latencySamples < 0) {
|
||||
return;
|
||||
}
|
||||
if(latencySamples < 4096) {
|
||||
// Latency can sometimes dip below this threshold
|
||||
samplesToDo = latencySamples;
|
||||
}
|
||||
var j = self.visAudioCursor - latencySamples
|
||||
let k = self.visAudioSize
|
||||
if j < 0 { j += k }
|
||||
for i in 0...4095 {
|
||||
let x = self.visAudio[j]
|
||||
outPCMCopy[i] = x
|
||||
j += 1; if j >= k { j = 0 }
|
||||
if(j + samplesToDo <= k) {
|
||||
outPCMCopy.replaceSubrange(0..<samplesToDo, with: self.visAudio.suffix(from: j).prefix(samplesToDo))
|
||||
} else {
|
||||
let outEndIndex = k - j
|
||||
let remainder = samplesToDo - outEndIndex
|
||||
outPCMCopy.replaceSubrange(0..<outEndIndex, with: self.visAudio.suffix(from: j))
|
||||
outPCMCopy.replaceSubrange(outEndIndex..<samplesToDo, with: self.visAudio.prefix(remainder))
|
||||
}
|
||||
}
|
||||
|
||||
if(outPCM != nil) {
|
||||
let pcmPointer = UnsafeMutableBufferPointer<Float>(start: outPCM, count: 4096)
|
||||
for i in 0...4095 {
|
||||
let x = outPCMCopy[i]
|
||||
pcmPointer[i] = x
|
||||
}
|
||||
}
|
||||
outPCM?.update(from: outPCMCopy, count: 4096)
|
||||
|
||||
if(visFFT != nil) {
|
||||
serialQueue.sync {
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22113.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22113.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="FeedbackController">
|
||||
<connections>
|
||||
<outlet property="fromView" destination="5" id="37"/>
|
||||
<outlet property="messageView" destination="15" id="38"/>
|
||||
<outlet property="sendingIndicator" destination="12" id="39"/>
|
||||
<outlet property="subjectView" destination="10" id="36"/>
|
||||
<outlet property="emailView" destination="10" id="X4O-Qx-zUq"/>
|
||||
<outlet property="messageView" destination="15" id="gwe-Bb-ZEz"/>
|
||||
<outlet property="nameView" destination="5" id="9Uz-Hh-EVf"/>
|
||||
<outlet property="window" destination="1" id="42"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Send Feedback" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" userLabel="FeedbackWindow">
|
||||
<window title="Send Crash Feedback" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" userLabel="FeedbackWindow">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="168" y="357" width="480" height="376"/>
|
||||
|
@ -27,7 +26,37 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="480" height="376"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="10">
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8" userLabel="Name:">
|
||||
<rect key="frame" x="17" y="339" width="58" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Name:" id="18" userLabel="Name:">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5" userLabel="Name Field">
|
||||
<rect key="frame" x="80" y="337" width="356" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="21">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<outlet property="nextKeyView" destination="10" id="CNG-sG-ab3"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4">
|
||||
<rect key="frame" x="17" y="297" width="58" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Email:" id="22">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="10" userLabel="Email Field">
|
||||
<rect key="frame" x="80" y="295" width="356" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="left" drawsBackground="YES" id="16">
|
||||
|
@ -36,22 +65,13 @@
|
|||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<outlet property="nextKeyView" destination="15" id="30"/>
|
||||
<outlet property="nextKeyView" destination="15" id="Xi7-6Y-bG1"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8">
|
||||
<rect key="frame" x="17" y="297" width="58" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Subject:" id="18">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7">
|
||||
<rect key="frame" x="17" y="262" width="66" height="17"/>
|
||||
<rect key="frame" x="17" y="262" width="272" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Message:" id="19">
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Describe what you were doing:" id="19">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -61,15 +81,15 @@
|
|||
<rect key="frame" x="20" y="55" width="440" height="199"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="tK9-bv-5OD">
|
||||
<rect key="frame" x="1" y="1" width="438" height="197"/>
|
||||
<rect key="frame" x="1" y="1" width="423" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView importsGraphics="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" continuousSpellChecking="YES" usesRuler="YES" smartInsertDelete="YES" id="15">
|
||||
<rect key="frame" x="0.0" y="0.0" width="438" height="197"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="423" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<size key="minSize" width="438" height="197"/>
|
||||
<size key="minSize" width="423" height="197"/>
|
||||
<size key="maxSize" width="863" height="10000000"/>
|
||||
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
</textView>
|
||||
|
@ -80,7 +100,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="14">
|
||||
<rect key="frame" x="423" y="1" width="16" height="197"/>
|
||||
<rect key="frame" x="424" y="1" width="15" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<connections>
|
||||
|
@ -111,31 +131,6 @@
|
|||
<outlet property="nextKeyView" destination="5" id="28"/>
|
||||
</connections>
|
||||
</button>
|
||||
<progressIndicator horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="12">
|
||||
<rect key="frame" x="444" y="340" width="16" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</progressIndicator>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5">
|
||||
<rect key="frame" x="80" y="337" width="356" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="21">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<outlet property="nextKeyView" destination="10" id="33"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4">
|
||||
<rect key="frame" x="17" y="339" width="71" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Email:" id="22">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22113.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22113.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -17,14 +17,35 @@
|
|||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
||||
<rect key="contentRect" x="700" y="80" width="300" height="604"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
|
||||
<value key="minSize" type="size" width="240" height="572"/>
|
||||
<value key="maxSize" type="size" width="400" height="600"/>
|
||||
<value key="minSize" type="size" width="240" height="594"/>
|
||||
<value key="maxSize" type="size" width="400" height="622"/>
|
||||
<view key="contentView" id="2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="604"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vB6-9J-5qg">
|
||||
<rect key="frame" x="-2" y="592" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Album Artist:" id="LFJ-QQ-gGr">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cj0-Tw-xpq" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="592" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" selectable="YES" sendsActionOnEndEditing="YES" title="N/A" id="B8w-o8-ZBw">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.albumartist" id="gTS-bf-rHT"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9">
|
||||
<rect key="frame" x="-2" y="548" width="107" height="14"/>
|
||||
<rect key="frame" x="-2" y="570" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Artist:" id="10">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
|
@ -32,98 +53,8 @@
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="11">
|
||||
<rect key="frame" x="-2" y="526" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Album:" id="12">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="13">
|
||||
<rect key="frame" x="-2" y="482" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Track:" id="14">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="15">
|
||||
<rect key="frame" x="-2" y="460" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Length:" id="16">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="17">
|
||||
<rect key="frame" x="-2" y="438" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Date:" id="18">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="19">
|
||||
<rect key="frame" x="-2" y="416" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Genre:" id="20">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="21">
|
||||
<rect key="frame" x="-2" y="372" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Sample Rate:" id="22">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="27">
|
||||
<rect key="frame" x="-2" y="350" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Channels:" id="28">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="29">
|
||||
<rect key="frame" x="-2" y="328" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Bitrate:" id="32">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="30">
|
||||
<rect key="frame" x="-2" y="306" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Bits Per Sample:" id="31">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="23">
|
||||
<rect key="frame" x="-2" y="504" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Title:" id="24">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="33" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="548" width="170" height="14"/>
|
||||
<rect key="frame" x="113" y="570" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" selectable="YES" sendsActionOnEndEditing="YES" title="N/A" id="34">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
|
@ -134,6 +65,36 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.artist" id="108"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fDr-Nh-3YI">
|
||||
<rect key="frame" x="-2" y="548" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Composer:" id="z22-KP-d7B">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WVx-cb-45q" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="548" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" selectable="YES" sendsActionOnEndEditing="YES" title="N/A" id="AUa-jh-Azn">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.composer" id="AKm-26-c5F"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="11">
|
||||
<rect key="frame" x="-2" y="526" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Album:" id="12">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="35" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="526" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -146,6 +107,15 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.album" id="109"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="23">
|
||||
<rect key="frame" x="-2" y="504" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Title:" id="24">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="37" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="504" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -158,6 +128,15 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.title" id="110"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="13">
|
||||
<rect key="frame" x="-2" y="482" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Track:" id="14">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="39" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="482" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -170,6 +149,15 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.trackText" id="ZO2-Cd-dfh"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="15">
|
||||
<rect key="frame" x="-2" y="460" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Length:" id="16">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="41" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="460" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -179,9 +167,19 @@
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="toolTip" keyPath="valueToDisplay.lengthInfo" id="26q-iJ-ecn"/>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.lengthText" id="ji7-tL-8rb"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="17">
|
||||
<rect key="frame" x="-2" y="438" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Date:" id="18">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="43" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="438" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -194,6 +192,15 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.date" id="EUq-4E-Lz3"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="19">
|
||||
<rect key="frame" x="-2" y="416" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Genre:" id="20">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="45" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="416" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -206,6 +213,36 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.genre" id="114"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="84">
|
||||
<rect key="frame" x="-2" y="394" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Filename:" id="85">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="86" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="394" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" selectable="YES" sendsActionOnEndEditing="YES" title="N/A" id="87">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.filename" id="115"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="21">
|
||||
<rect key="frame" x="-2" y="372" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Sample Rate:" id="22">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="49" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="372" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -222,6 +259,15 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="27">
|
||||
<rect key="frame" x="-2" y="350" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Channels:" id="28">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="51" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="350" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -234,6 +280,15 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.channels" id="117"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="29">
|
||||
<rect key="frame" x="-2" y="328" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Bitrate:" id="32">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="53" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="328" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -246,6 +301,15 @@
|
|||
<binding destination="-2" name="value" keyPath="valueToDisplay.bitrate" id="118"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="30">
|
||||
<rect key="frame" x="-2" y="306" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Bits Per Sample:" id="31">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="55" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="306" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -343,48 +407,6 @@
|
|||
<binding destination="-2" name="toolTip" keyPath="valueToDisplay.gainInfo" id="BUp-Hu-Szq"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="84">
|
||||
<rect key="frame" x="-2" y="394" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Filename:" id="85">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="86" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="394" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" selectable="YES" sendsActionOnEndEditing="YES" title="N/A" id="87">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.filename" id="115"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vB6-9J-5qg">
|
||||
<rect key="frame" x="-2" y="570" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Album Artist:" id="LFJ-QQ-gGr">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cj0-Tw-xpq" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="570" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" selectable="YES" sendsActionOnEndEditing="YES" title="N/A" id="B8w-o8-ZBw">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="valueToDisplay.albumartist" id="gTS-bf-rHT"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FWx-Sc-Ocv">
|
||||
<rect key="frame" x="-2" y="196" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
|
@ -394,15 +416,6 @@
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cd3-Qt-hCm">
|
||||
<rect key="frame" x="-2" y="174" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Comment:" id="Ule-N3-dKW">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WSs-wC-mWc" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="196" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
@ -416,6 +429,15 @@
|
|||
<binding destination="-2" name="toolTip" keyPath="valueToDisplay.playCountInfo" id="ydF-ec-fBX"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cd3-Qt-hCm">
|
||||
<rect key="frame" x="-2" y="174" width="107" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Comment:" id="Ule-N3-dKW">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ef3-yG-qT1" customClass="ToolTipTextField">
|
||||
<rect key="frame" x="113" y="174" width="170" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22113.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22113.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -17,31 +17,31 @@
|
|||
<window title="Cog" separatorStyle="none" allowsToolTipsWhenApplicationIsInactive="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="Cog" animationBehavior="default" tabbingMode="disallowed" toolbarStyle="unified" id="21" userLabel="Window" customClass="MainWindow">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
||||
<rect key="contentRect" x="331" y="367" width="1000" height="400"/>
|
||||
<rect key="contentRect" x="331" y="367" width="1281" height="400"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
|
||||
<value key="minSize" type="size" width="750" height="200"/>
|
||||
<stackView key="contentView" distribution="equalSpacing" orientation="vertical" alignment="centerX" spacing="0.0" misplaced="YES" detachesHiddenViews="YES" id="2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1000" height="400"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1281" height="400"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123">
|
||||
<rect key="frame" x="0.0" y="372" width="1135" height="28"/>
|
||||
<rect key="frame" x="0.0" y="349" width="1274" height="51"/>
|
||||
<subviews>
|
||||
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1135" height="28"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1274" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1135" height="28"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1274" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1135" height="24"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1274" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="6"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="index" editable="NO" width="64" minWidth="28" maxWidth="64" id="209">
|
||||
<tableColumn identifier="index" editable="NO" width="62" minWidth="28" maxWidth="64" id="209">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="#">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
@ -54,11 +54,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="5N3-SP-Y8z">
|
||||
<rect key="frame" x="11" y="3" width="69" height="18"/>
|
||||
<rect key="frame" x="11" y="3" width="67" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="w5u-JQ-3Hf">
|
||||
<rect key="frame" x="0.0" y="1" width="69" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="67" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="right" title="Table View Cell" id="FMU-QZ-NdQ">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -95,7 +95,7 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="Vw5-xt-0vG">
|
||||
<rect key="frame" x="83" y="3" width="20" height="17"/>
|
||||
<rect key="frame" x="81" y="3" width="20" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ERj-i9-caa">
|
||||
|
@ -127,7 +127,7 @@
|
|||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="rating" editable="NO" width="64" minWidth="48" maxWidth="128" id="208" userLabel="Rating">
|
||||
<tableColumn identifier="rating" editable="NO" width="114" minWidth="48" maxWidth="128" id="208" userLabel="Rating">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Rating">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
@ -141,11 +141,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="ZCP-Dx-UBV">
|
||||
<rect key="frame" x="106" y="3" width="64" height="18"/>
|
||||
<rect key="frame" x="104" y="3" width="114" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="exY-Bg-Mjm">
|
||||
<rect key="frame" x="0.0" y="1" width="64" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="114" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="sdo-Sm-KPH">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -171,7 +171,7 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="title" editable="NO" width="126" minWidth="96" maxWidth="1024" id="XBr-ec-D81">
|
||||
<tableColumn identifier="title" editable="NO" width="177" minWidth="96" maxWidth="1024" id="XBr-ec-D81">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Title">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
@ -185,11 +185,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="ZHl-H1-IIC">
|
||||
<rect key="frame" x="173" y="3" width="126" height="18"/>
|
||||
<rect key="frame" x="221" y="3" width="177" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="dQP-wC-mba">
|
||||
<rect key="frame" x="0.0" y="1" width="126" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="177" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="VVx-99-roJ">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -259,7 +259,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="dJs-UO-m5r"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="artist" editable="NO" width="150" minWidth="96" maxWidth="1024" id="391">
|
||||
<tableColumn identifier="artist" editable="NO" width="200" minWidth="96" maxWidth="1024" id="391">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -273,11 +273,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="gpC-Oe-Rog">
|
||||
<rect key="frame" x="302" y="3" width="150" height="18"/>
|
||||
<rect key="frame" x="401" y="3" width="200" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="1WK-qN-Mgj">
|
||||
<rect key="frame" x="0.0" y="1" width="150" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="200" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="71l-3L-S3g">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -303,7 +303,51 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="album" editable="NO" width="151" minWidth="96" maxWidth="1024" id="806">
|
||||
<tableColumn identifier="composer" editable="NO" width="151" minWidth="96" maxWidth="1024" hidden="YES" id="B9Z-OR-rQP" userLabel="Composer">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Composer">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="JX7-wV-kUU">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<sortDescriptor key="sortDescriptorPrototype" selector="caseInsensitiveCompare:" sortKey="composer"/>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView misplaced="YES" id="dgP-m4-g1z">
|
||||
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="4cz-aa-d2B">
|
||||
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="DDa-1D-XYS">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="4cz-aa-d2B" firstAttribute="leading" secondItem="dgP-m4-g1z" secondAttribute="leading" constant="2" id="4Gd-Da-cTb"/>
|
||||
<constraint firstItem="4cz-aa-d2B" firstAttribute="centerY" secondItem="dgP-m4-g1z" secondAttribute="centerY" id="MBR-lu-Sqm"/>
|
||||
<constraint firstItem="4cz-aa-d2B" firstAttribute="centerX" secondItem="dgP-m4-g1z" secondAttribute="centerX" id="rWE-oT-geg"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="4cz-aa-d2B" id="bar-OG-9cs"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="218" name="value" keyPath="arrangedObjects.composer" id="MiB-Bd-qrj">
|
||||
<dictionary key="options">
|
||||
<bool key="NSConditionallySetsEditable" value="YES"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="album" editable="NO" width="200.5" minWidth="96" maxWidth="1024" id="806">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Album">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -317,11 +361,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="1ed-gX-bct">
|
||||
<rect key="frame" x="455" y="3" width="151" height="18"/>
|
||||
<rect key="frame" x="604" y="3" width="201" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nEt-s5-vRX">
|
||||
<rect key="frame" x="0.0" y="1" width="151" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="201" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="moV-3G-GpB">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -347,7 +391,7 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="length" editable="NO" width="96" minWidth="43.62012" maxWidth="96" id="807">
|
||||
<tableColumn identifier="length" editable="NO" width="93.5" minWidth="43.62012" maxWidth="96" id="807">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="Length">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -360,11 +404,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="hhB-nv-e78">
|
||||
<rect key="frame" x="609" y="3" width="96" height="18"/>
|
||||
<rect key="frame" x="808" y="3" width="93" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tHy-sM-HDB">
|
||||
<rect key="frame" x="0.0" y="1" width="96" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="94" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Igo-5f-yim">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -391,7 +435,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1919"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="year" editable="NO" width="94" minWidth="42" maxWidth="96" id="848">
|
||||
<tableColumn identifier="year" editable="NO" width="93.5" minWidth="42" maxWidth="96" id="848">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="Year">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -404,7 +448,7 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="q93-oh-i5T">
|
||||
<rect key="frame" x="708" y="3" width="94" height="18"/>
|
||||
<rect key="frame" x="904" y="3" width="94" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="bOi-LI-TDx">
|
||||
|
@ -431,7 +475,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1921"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="genre" editable="NO" width="64" minWidth="32" maxWidth="512" id="849">
|
||||
<tableColumn identifier="genre" editable="NO" width="112.5" minWidth="32" maxWidth="512" id="849">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Genre">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -445,11 +489,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="rRl-p9-Awr">
|
||||
<rect key="frame" x="805" y="3" width="64" height="18"/>
|
||||
<rect key="frame" x="1001" y="3" width="112" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yW6-2w-6mN">
|
||||
<rect key="frame" x="0.0" y="1" width="64" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="113" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="js2-sT-U4M">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -472,7 +516,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1922"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="track" editable="NO" width="35" minWidth="24" maxWidth="72" id="850">
|
||||
<tableColumn identifier="track" editable="NO" width="69.5" minWidth="24" maxWidth="72" id="850">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="№">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -485,11 +529,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="hgh-VE-5kl">
|
||||
<rect key="frame" x="872" y="3" width="39" height="18"/>
|
||||
<rect key="frame" x="1116" y="3" width="70" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yEY-MI-d3o">
|
||||
<rect key="frame" x="0.0" y="1" width="39" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="70" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="tus-lr-RhS">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -512,6 +556,50 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1923"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="playcount" editable="NO" width="69.5" minWidth="24" maxWidth="72" id="1g1-Th-emL" userLabel="Play Count">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="Play Count">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="right" title="Text Cell" id="Bhh-hp-xx3">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="aED-2V-LLM">
|
||||
<rect key="frame" x="1189" y="3" width="73" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tYz-2J-JRx">
|
||||
<rect key="frame" x="0.0" y="1" width="74" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="HJL-T1-ufx">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="tYz-2J-JRx" firstAttribute="centerX" secondItem="aED-2V-LLM" secondAttribute="centerX" id="37Y-n2-p29"/>
|
||||
<constraint firstItem="tYz-2J-JRx" firstAttribute="leading" secondItem="aED-2V-LLM" secondAttribute="leading" constant="2" id="4gs-nP-xUS"/>
|
||||
<constraint firstItem="tYz-2J-JRx" firstAttribute="centerY" secondItem="aED-2V-LLM" secondAttribute="centerY" id="SjU-Ta-4Sc"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="tYz-2J-JRx" id="bNh-Jc-1yQ"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="218" name="value" keyPath="arrangedObjects.playCount" id="6s9-cV-iYx">
|
||||
<dictionary key="options">
|
||||
<bool key="NSConditionallySetsEditable" value="YES"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="iAQ-bR-gvW"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="path" editable="NO" width="64" minWidth="32" maxWidth="2048" hidden="YES" id="1712">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Path">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -732,6 +820,50 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="OsB-l4-dFb"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="bitrate" editable="NO" width="64" minWidth="32" maxWidth="1024" hidden="YES" id="PoD-EU-L4X" userLabel="Bitrate">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="Bitrate">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="9df-mL-Zgx">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="Wqm-GT-K5a">
|
||||
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="L7E-1S-m3b">
|
||||
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="cAf-1y-D7M">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="L7E-1S-m3b" secondAttribute="trailing" constant="-2" id="Fn8-gt-4io"/>
|
||||
<constraint firstItem="L7E-1S-m3b" firstAttribute="leading" secondItem="Wqm-GT-K5a" secondAttribute="leading" constant="2" id="GDJ-WW-3Vb"/>
|
||||
<constraint firstItem="L7E-1S-m3b" firstAttribute="centerY" secondItem="Wqm-GT-K5a" secondAttribute="centerY" id="O5A-dw-6sx"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="L7E-1S-m3b" id="biP-XR-a2T"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
<connections>
|
||||
<binding destination="218" name="value" keyPath="arrangedObjects.bitrate" id="EwM-t3-oO8">
|
||||
<dictionary key="options">
|
||||
<bool key="NSConditionallySetsEditable" value="YES"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="eFT-ZC-67y"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<connections>
|
||||
<binding destination="1689" name="rowHeight" keyPath="values.fontSize" id="1927">
|
||||
|
@ -756,11 +888,11 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="1515">
|
||||
<rect key="frame" x="85" y="17" width="15" height="68"/>
|
||||
<rect key="frame" x="1209" y="17" width="15" height="11"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<tableHeaderView key="headerView" wantsLayer="YES" id="1517">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1135" height="17"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1274" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableHeaderView>
|
||||
</scrollView>
|
||||
|
@ -773,7 +905,7 @@
|
|||
</connections>
|
||||
</splitView>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="778">
|
||||
<rect key="frame" x="437" y="4" width="261" height="14"/>
|
||||
<rect key="frame" x="507" y="4" width="261" height="14"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Total Duration: 00 hours 00 minutes 00 seconds" bezelStyle="round" id="1473">
|
||||
<font key="font" metaFont="controlContent" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -808,7 +940,7 @@
|
|||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<toolbar key="toolbar" implicitIdentifier="B4998081-90DD-45DD-8243-0F7039C7DEA2" displayMode="iconOnly" sizeMode="regular" id="1523">
|
||||
<toolbar key="toolbar" implicitIdentifier="B4998081-90DD-45DD-8243-0F7039C7DEA2" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="1523">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="1552"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="1529"/>
|
||||
|
@ -910,7 +1042,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="3B680DEB-106E-4549-A478-FFB8A6738053" label="Volume" paletteLabel="Volume" image="volume3Template" visibilityPriority="10" sizingBehavior="auto" id="1610">
|
||||
<toolbarItem implicitItemIdentifier="3B680DEB-106E-4549-A478-FFB8A6738053" label="Volume" paletteLabel="Volume" image="volume3Template" bordered="YES" visibilityPriority="10" sizingBehavior="auto" id="1610">
|
||||
<nil key="toolTip"/>
|
||||
<button key="view" verticalHuggingPriority="750" id="1608" customClass="VolumeButton">
|
||||
<rect key="frame" x="9" y="14" width="29" height="23"/>
|
||||
|
@ -924,6 +1056,24 @@
|
|||
</connections>
|
||||
</button>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="7BC995AD-3F93-40E1-AE76-94574D34BCB0" label="Speed" paletteLabel="Speed" image="deskclock" catalog="system" bordered="YES" visibilityPriority="10" sizingBehavior="auto" id="ufn-od-xJF" userLabel="Speed">
|
||||
<nil key="toolTip"/>
|
||||
<button key="view" verticalHuggingPriority="750" id="Ta5-Ik-jh9" userLabel="Speed Button" customClass="SpeedButton">
|
||||
<rect key="frame" x="7" y="14" width="27" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="deskclock" catalog="system" imagePosition="only" alignment="center" borderStyle="border" inset="2" id="2Al-Cl-n36">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<outlet property="_LockButton" destination="OQS-2p-1yP" id="F1I-fb-SDQ"/>
|
||||
<outlet property="_PitchSlider" destination="6P4-yi-9TK" id="Ni0-G0-USM"/>
|
||||
<outlet property="_ResetButton" destination="3Zc-Xv-g24" id="C7x-EU-QpA"/>
|
||||
<outlet property="_TempoSlider" destination="stI-oD-51s" id="Mt0-7i-R4f"/>
|
||||
<outlet property="_popView" destination="90w-7t-RYP" id="qfi-e0-UZc"/>
|
||||
</connections>
|
||||
</button>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="2F487D99-16E9-4BF8-9A98-637FABEB2716" label="Info Inspector" paletteLabel="Info Inspector" image="infoTemplate" navigational="YES" id="1629">
|
||||
<nil key="toolTip"/>
|
||||
<size key="minSize" width="28" height="23"/>
|
||||
|
@ -1087,6 +1237,7 @@
|
|||
<toolbarItem reference="ZH9-ZU-skw"/>
|
||||
<toolbarItem reference="1539"/>
|
||||
<toolbarItem reference="1610"/>
|
||||
<toolbarItem reference="ufn-od-xJF"/>
|
||||
<toolbarItem reference="1552"/>
|
||||
<toolbarItem reference="1568"/>
|
||||
<toolbarItem reference="1551"/>
|
||||
|
@ -1116,7 +1267,7 @@
|
|||
<outlet property="mainToolbar" destination="1523" id="J2X-YK-xV0"/>
|
||||
<outlet property="playlistView" destination="206" id="mah-wo-1nd"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="130" y="131"/>
|
||||
<point key="canvasLocation" x="270.5" y="131"/>
|
||||
</window>
|
||||
<window title="Cog" separatorStyle="none" allowsToolTipsWhenApplicationIsInactive="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="Mini Window" animationBehavior="default" toolbarStyle="unified" id="2234" userLabel="Mini Window (Window)" customClass="MiniWindow">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
|
@ -1126,7 +1277,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="581" height="0.0"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="35998ECE-5AD8-429E-8479-657249B22C9C" displayMode="iconOnly" sizeMode="regular" id="2222" userLabel="Mini Toolbar">
|
||||
<toolbar key="toolbar" implicitIdentifier="35998ECE-5AD8-429E-8479-657249B22C9C" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="2222" userLabel="Mini Toolbar">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="2227"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="2228"/>
|
||||
|
@ -1380,11 +1531,6 @@
|
|||
<action selector="showWindow:" target="Hd4-Wy-Rfl" id="xfd-8T-SL4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check for Updates..." id="302">
|
||||
<connections>
|
||||
<action selector="checkForUpdates:" target="226" id="jEY-i9-qwZ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="1100">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
|
@ -1395,45 +1541,6 @@
|
|||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="0ig-xg-gkg"/>
|
||||
<menuItem title="Donate" id="751">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Donate" id="kue-p2-G0Y">
|
||||
<items>
|
||||
<menuItem title="LiberaPay" id="nyW-nI-abw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="openLiberapayPage:" target="226" id="pS6-Hj-tIm"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Mbf-yW-WGC"/>
|
||||
<menuItem title="One time" enabled="NO" id="wLp-NA-5u2">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="PayPal" id="xcs-tx-Viz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="openPaypalPage:" target="226" id="oxr-P1-35O"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="txC-Jd-Gez"/>
|
||||
<menuItem title="Recurring" enabled="NO" id="iRb-7e-iMC">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Ko-fi" id="CVM-rp-UJe">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="openKofiPage:" target="226" id="gax-6q-SuW"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Patreon" id="NXj-oA-q3F">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="openPatreonPage:" target="226" id="LFz-4J-b6o"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Send Feedback..." id="303">
|
||||
<connections>
|
||||
<action selector="feedback:" target="226" id="GSH-G5-qM1"/>
|
||||
|
@ -1564,6 +1671,12 @@
|
|||
<action selector="scrollToCurrentEntry:" target="207" id="1888"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Selection Follows Playback" id="Q7K-Eu-1Vf">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<binding destination="1689" name="value" keyPath="values.selectionFollowsPlayback" id="pml-0X-qIh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
@ -1775,6 +1888,12 @@
|
|||
<action selector="stopAfterCurrent:" target="218" id="1896"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Always Stop After Current" keyEquivalent="." id="y8c-n8-5Uh">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<binding destination="1689" name="value" keyPath="values.alwaysStopAfterCurrent" id="rwr-s6-PnE"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
@ -1919,17 +2038,11 @@ CA
|
|||
<menuItem title="Repeat" id="1740">
|
||||
<menu key="submenu" title="Repeat" id="1741">
|
||||
<items>
|
||||
<menuItem title="All" keyEquivalent="r" id="1742">
|
||||
<menuItem title="All" state="on" keyEquivalent="r" id="1742">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<binding destination="218" name="value" keyPath="repeat" id="1760">
|
||||
<dictionary key="options">
|
||||
<integer key="NSAllowsEditingMultipleValuesSelection" value="0"/>
|
||||
<integer key="NSAlwaysPresentsApplicationModalAlerts" value="0"/>
|
||||
<integer key="NSConditionallySetsEnabled" value="0"/>
|
||||
<integer key="NSConditionallySetsHidden" value="0"/>
|
||||
<integer key="NSRaisesForNotApplicableKeys" value="1"/>
|
||||
<integer key="NSValidatesImmediately" value="0"/>
|
||||
<string key="NSValueTransformerName">RepeatAllTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
|
@ -1939,8 +2052,6 @@ CA
|
|||
<connections>
|
||||
<binding destination="218" name="value" keyPath="repeat" id="2412">
|
||||
<dictionary key="options">
|
||||
<bool key="NSAllowsEditingMultipleValuesSelection" value="NO"/>
|
||||
<bool key="NSConditionallySetsEnabled" value="NO"/>
|
||||
<string key="NSValueTransformerName">RepeatAlbumTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
|
@ -1950,12 +2061,6 @@ CA
|
|||
<connections>
|
||||
<binding destination="218" name="value" keyPath="repeat" id="1763">
|
||||
<dictionary key="options">
|
||||
<integer key="NSAllowsEditingMultipleValuesSelection" value="0"/>
|
||||
<integer key="NSAlwaysPresentsApplicationModalAlerts" value="0"/>
|
||||
<integer key="NSConditionallySetsEnabled" value="0"/>
|
||||
<integer key="NSConditionallySetsHidden" value="0"/>
|
||||
<integer key="NSRaisesForNotApplicableKeys" value="1"/>
|
||||
<integer key="NSValidatesImmediately" value="0"/>
|
||||
<string key="NSValueTransformerName">RepeatOneTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
|
@ -1965,12 +2070,6 @@ CA
|
|||
<connections>
|
||||
<binding destination="218" name="value" keyPath="repeat" id="1767">
|
||||
<dictionary key="options">
|
||||
<integer key="NSAllowsEditingMultipleValuesSelection" value="0"/>
|
||||
<integer key="NSAlwaysPresentsApplicationModalAlerts" value="0"/>
|
||||
<integer key="NSConditionallySetsEnabled" value="0"/>
|
||||
<integer key="NSConditionallySetsHidden" value="0"/>
|
||||
<integer key="NSRaisesForNotApplicableKeys" value="1"/>
|
||||
<integer key="NSValidatesImmediately" value="0"/>
|
||||
<string key="NSValueTransformerName">RepeatNoneTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
|
@ -2154,6 +2253,7 @@ Gw
|
|||
<string>path</string>
|
||||
<string>status</string>
|
||||
<mutableString>queued</mutableString>
|
||||
<string>playCount</string>
|
||||
</declaredKeys>
|
||||
<classReference key="objectClass" className="PlaylistEntry"/>
|
||||
<connections>
|
||||
|
@ -2316,9 +2416,12 @@ Gw
|
|||
<connections>
|
||||
<outlet property="appController" destination="226" id="TnP-DA-nJl"/>
|
||||
<outlet property="equalizerWindowController" destination="dJ9-b3-BFu" id="gB5-Bu-vqC"/>
|
||||
<outlet property="lockButton" destination="OQS-2p-1yP" id="RfR-WH-ZPY"/>
|
||||
<outlet property="pitchSlider" destination="6P4-yi-9TK" id="Lnu-kw-iYW"/>
|
||||
<outlet property="playlistController" destination="218" id="706"/>
|
||||
<outlet property="playlistLoader" destination="1319" id="ghZ-65-60L"/>
|
||||
<outlet property="playlistView" destination="207" id="717"/>
|
||||
<outlet property="tempoSlider" destination="stI-oD-51s" id="dDc-lb-crL"/>
|
||||
<outlet property="volumeSlider" destination="1612" id="1615"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
|
@ -2498,6 +2601,107 @@ Gw
|
|||
</subviews>
|
||||
<point key="canvasLocation" x="615" y="-25"/>
|
||||
</customView>
|
||||
<customView id="90w-7t-RYP" userLabel="Speed View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="69" height="216"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uVG-0w-rW4">
|
||||
<rect key="frame" x="4" y="199" width="24" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="🎵" id="bQV-ZD-SDv">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VUX-JL-rjr">
|
||||
<rect key="frame" x="41" y="199" width="24" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="⏰" id="4R4-ba-Ndx">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<slider horizontalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6P4-yi-9TK" userLabel="Pitch Slider" customClass="PitchSlider">
|
||||
<rect key="frame" x="6" y="34" width="20" height="164"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="40.824829049999998" tickMarkPosition="left" sliderType="linear" id="vTw-tV-W5R"/>
|
||||
<connections>
|
||||
<action selector="changePitch:" target="705" id="bZt-zY-tjg"/>
|
||||
<binding destination="1689" name="enabled" keyPath="values.rubberbandEngine" id="3Kk-ix-XCv">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">RubberbandEngineEnabledTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<outlet property="_TempoSlider" destination="stI-oD-51s" id="HMq-pE-Ssc"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OQS-2p-1yP" userLabel="Lock Button">
|
||||
<rect key="frame" x="17" y="101" width="36" height="27"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="🔒" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="M0v-A9-meu">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressLock:" target="Ta5-Ik-jh9" id="HnF-AC-arz"/>
|
||||
<binding destination="1689" name="enabled" keyPath="values.rubberbandEngine" id="BUK-i6-qzt">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">RubberbandEngineEnabledTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</button>
|
||||
<slider horizontalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="stI-oD-51s" userLabel="Tempo Slider" customClass="TempoSlider">
|
||||
<rect key="frame" x="42" y="34" width="20" height="164"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" maxValue="100" doubleValue="40.824829049999998" tickMarkPosition="left" sliderType="linear" id="94j-7B-a8j"/>
|
||||
<connections>
|
||||
<action selector="changeTempo:" target="705" id="8xu-Dm-ceG"/>
|
||||
<binding destination="1689" name="enabled" keyPath="values.rubberbandEngine" id="eRW-kg-FQc">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">RubberbandEngineEnabledTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<outlet property="_PitchSlider" destination="6P4-yi-9TK" id="QaV-cx-2wf"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Zc-Xv-g24" userLabel="Reset Button">
|
||||
<rect key="frame" x="2" y="2" width="64" height="27"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="1.0×" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ijs-k0-EIP">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressReset:" target="Ta5-Ik-jh9" id="zoi-N3-teA"/>
|
||||
<binding destination="1689" name="enabled" keyPath="values.rubberbandEngine" id="GU1-d5-uWi">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">RubberbandEngineEnabledTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yQh-Rt-nJL" userLabel="Notice">
|
||||
<rect key="frame" x="0.0" y="57" width="66" height="27"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="⚠️" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XV7-Re-I9u">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showRubberbandSettings:" target="226" id="zqd-7k-1f0"/>
|
||||
<binding destination="1689" name="hidden" keyPath="values.rubberbandEngine" id="BoK-zT-W4T">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">RubberbandEngineHiddenTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="964.5" y="12"/>
|
||||
</customView>
|
||||
<customObject id="1675" customClass="SpotlightWindowController">
|
||||
<connections>
|
||||
<outlet property="playlistLoader" destination="1319" id="1694"/>
|
||||
|
@ -2563,6 +2767,7 @@ Gw
|
|||
</objects>
|
||||
<resources>
|
||||
<image name="deadItemsTemplate" width="20" height="20"/>
|
||||
<image name="deskclock" catalog="system" width="15" height="16"/>
|
||||
<image name="duplicateItemsTemplate" width="20" height="20"/>
|
||||
<image name="equalizerTemplate" width="20" height="20"/>
|
||||
<image name="hdcdLogoTemplate" width="656" height="225"/>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22113.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22113.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string>
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
|
|
3
Cog.sdef
3
Cog.sdef
|
@ -147,6 +147,9 @@
|
|||
<property name="artist" code="pArt" description="The artist of the entry" type="text">
|
||||
<cocoa key="artist" insert-at-beginning="yes"/>
|
||||
</property>
|
||||
<property name="composer" code="pCom" description="The composer of the entry" type="text">
|
||||
<cocoa key="composer" insert-at-beginning="yes"/>
|
||||
</property>
|
||||
<property name="title" code="pTit" description="The title of the entry." type="text" access="r">
|
||||
<cocoa key="title" insert-at-beginning="yes"/>
|
||||
</property>
|
||||
|
|
|
@ -98,13 +98,11 @@
|
|||
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; };
|
||||
830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; };
|
||||
831B99BF27C23E88005A969B /* Cog.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 831B99BE27C23E88005A969B /* Cog.sdef */; };
|
||||
83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83229C9D283B0095004626A8 /* SpectrumWindowController.m */; };
|
||||
83256B68286661FC0036D9C0 /* libmpg123.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; };
|
||||
83256B69286661FC0036D9C0 /* libmpg123.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8327DB94293C923500CD0580 /* Organya.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
832923AF279FAC400048201E /* Cog.q1.json in Resources */ = {isa = PBXBuildFile; fileRef = 832923AE279FAC400048201E /* Cog.q1.json */; };
|
||||
832CFC4F2851AA1A002AC26F /* NSView+Visibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 832CFC4E2851AA1A002AC26F /* NSView+Visibility.m */; };
|
||||
832CFC562851AA8B002AC26F /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 832CFC552851AA8B002AC26F /* SpectrumViewCG.m */; };
|
||||
833D0C2527C4ABB80060E16A /* ScriptAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 833D0C2427C4ABB80060E16A /* ScriptAdditions.m */; };
|
||||
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83489C542782F2DF00BDCEA2 /* libvgmPlayer.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
834B05EA2859C006000B7DC0 /* TotalTimeTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 834B05E92859C006000B7DC0 /* TotalTimeTransformer.m */; };
|
||||
|
@ -138,36 +136,40 @@
|
|||
836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D73C277419F700245CE0 /* SQLiteStore.m */; };
|
||||
8370D73F2775AE1300245CE0 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8370D73E2775AE1300245CE0 /* libsqlite3.tbd */; };
|
||||
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8372C93027C785BE00E250C9 /* MAD.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
8377C66327B8CF6300E8BC0F /* SpectrumViewSK.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */; };
|
||||
8377C6B927B900F000E8BC0F /* SpectrumItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C6B827B900F000E8BC0F /* SpectrumItem.m */; };
|
||||
837DC92B285B05710005C58A /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 837DC92A285B05710005C58A /* CoreData.framework */; };
|
||||
837DC931285B3F790005C58A /* DataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */; };
|
||||
8381A09227C5F72F00A1C530 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8381A09127C5F72F00A1C530 /* SHA256Digest.m */; };
|
||||
8384914018083E4E00E7332D /* filetype.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913D18083E4E00E7332D /* filetype.icns */; };
|
||||
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; };
|
||||
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
838770FA2D6F0996001455A0 /* FeedbackController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838770F42D6F0996001455A0 /* FeedbackController.m */; };
|
||||
838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 838A33732D06A9B100D0D770 /* librubberband.3.dylib */; };
|
||||
838A33752D06A9CE00D0D770 /* librubberband.3.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838A33732D06A9B100D0D770 /* librubberband.3.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
838A337D2D06C14200D0D770 /* TempoSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A337C2D06C14200D0D770 /* TempoSlider.m */; };
|
||||
838A337E2D06C14200D0D770 /* PitchSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A337A2D06C14200D0D770 /* PitchSlider.m */; };
|
||||
838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33802D06CF4100D0D770 /* SpectrumViewCG.m */; };
|
||||
838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33822D06CF4100D0D770 /* SpectrumWindowController.m */; };
|
||||
838A33872D06CFCA00D0D770 /* SpeedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33862D06CFCA00D0D770 /* SpeedButton.m */; };
|
||||
83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83922FB6286B1AA900A0B039 /* WebKit.framework */; };
|
||||
839614A2286ED97200D3EEDB /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614A0286ED97200D3EEDB /* AboutWindowController.xib */; };
|
||||
839614AD286EDA5C00D3EEDB /* SpectrumWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614AB286EDA5C00D3EEDB /* SpectrumWindow.xib */; };
|
||||
83978E16285C58190076ED21 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 83978E15285C58190076ED21 /* FirebaseCrashlytics */; };
|
||||
83978E26285C596F0076ED21 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 83978E25285C596F0076ED21 /* FirebaseAnalytics */; };
|
||||
83978E29285C5C0A0076ED21 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 83978E28285C5C0A0076ED21 /* GoogleService-Info.plist */; };
|
||||
83988F0E27BE0A5900A0E89A /* RedundantPlaylistDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */; };
|
||||
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; };
|
||||
839B837F286D7F8D00F529EE /* NumberHertzToStringTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839B837E286D7F8D00F529EE /* NumberHertzToStringTransformer.swift */; };
|
||||
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */; };
|
||||
839E56F52879625100DFB5F4 /* SADIE_D02-96000.mhr in Resources */ = {isa = PBXBuildFile; fileRef = 839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */; };
|
||||
839E876E2D5DA0AC00A13526 /* RubberbandEngineTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 839E876D2D5DA0AC00A13526 /* RubberbandEngineTransformer.m */; };
|
||||
83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */; };
|
||||
83AA7D04279EBCA900087AA4 /* libavcodec.59.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D05279EBCAB00087AA4 /* libavformat.59.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D03279EBC8300087AA4 /* libavformat.59.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D06279EBCAD00087AA4 /* libavutil.57.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D02279EBC8200087AA4 /* libavutil.57.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D07279EBCAF00087AA4 /* libswresample.4.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D01279EBC8200087AA4 /* libswresample.4.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D04279EBCA900087AA4 /* libavcodec.61.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D00279EBC8200087AA4 /* libavcodec.61.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D05279EBCAB00087AA4 /* libavformat.61.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D03279EBC8300087AA4 /* libavformat.61.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D06279EBCAD00087AA4 /* libavutil.59.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D02279EBC8200087AA4 /* libavutil.59.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83AA7D07279EBCAF00087AA4 /* libswresample.5.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D01279EBC8200087AA4 /* libswresample.5.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066A1180D5669008E3612 /* MIDI.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83B61E2429A8296500CD0580 /* LyricsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83B61E2229A8296500CD0580 /* LyricsWindow.xib */; };
|
||||
83B61E2829A82A0200CD0580 /* LyricsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B61E2729A82A0200CD0580 /* LyricsWindowController.m */; };
|
||||
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B72E2A279044F6006007A3 /* libfdk-aac.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B73B602D8FC05A00A57F08 /* minimp3.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BC5AB020E4C87100631CD4 /* DualWindow.m */; };
|
||||
83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; };
|
||||
83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342A980D5FD20B00E8D854 /* MainMenu.xib */; };
|
||||
|
@ -176,10 +178,10 @@
|
|||
83BC5AC320E4CE8D00631CD4 /* SpotlightPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 178456C00F6320B5007E8021 /* SpotlightPanel.xib */; };
|
||||
83BC5AC420E4CE9000631CD4 /* Feedback.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B1DA0F6330D400694C57 /* Feedback.xib */; };
|
||||
83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
83CB565E2E064607003DD379 /* NSDictionary+Optional.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CB565D2E064607003DD379 /* NSDictionary+Optional.m */; };
|
||||
83D0380F24A40DFB004CF90F /* CogAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */; };
|
||||
83F7AADA290B682400951B61 /* scpipe in Resources */ = {isa = PBXBuildFile; fileRef = 83F7AAD8290B682400951B61 /* scpipe */; };
|
||||
83F7AADE290B8DDF00951B61 /* IIAM.bin in Resources */ = {isa = PBXBuildFile; fileRef = 83F7AAD7290B682400951B61 /* IIAM.bin */; };
|
||||
83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F9D7F61A884B46007ABEC2 /* SilenceDecoder.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 83F9FFEE2D6EB75B00026576 /* Sentry */; };
|
||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
||||
|
@ -194,7 +196,7 @@
|
|||
8E8D41C80CBB0DA900135C1B /* Pls.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8E8D41C70CBB0DA000135C1B /* Pls.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E9A30140BA792DC0091081B /* NSFileHandle+CreateFile.m */; };
|
||||
99EAACA80DD1BB7A00423C38 /* APL.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 566D321B0D538550004466A5 /* APL.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F5622E0C3BD8FB0019975C /* Preferences.preferencePane */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in Copy Files */ = {isa = PBXBuildFile; fileRef = 17F5622E0C3BD8FB0019975C /* Preferences.preferencePane */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
ED69CBC725BE32C00090B90D /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED69CBBF25BE328C0090B90D /* MASShortcut.framework */; };
|
||||
ED69CBCA25BE32E80090B90D /* MASShortcut.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = ED69CBBF25BE328C0090B90D /* MASShortcut.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
EDAAA41F25A665C000731773 /* PositionSliderToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAAA41E25A665C000731773 /* PositionSliderToolbarItem.swift */; };
|
||||
|
@ -510,20 +512,6 @@
|
|||
remoteGlobalIDString = 836FB52C1820538700B3AD2D;
|
||||
remoteInfo = Hively;
|
||||
};
|
||||
8372C92F27C785BE00E250C9 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 8372C92327C785BD00E250C9;
|
||||
remoteInfo = MAD;
|
||||
};
|
||||
8372C93B27C7893100E250C9 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 8372C92227C785BD00E250C9;
|
||||
remoteInfo = MAD;
|
||||
};
|
||||
8375B36117FFEF010092A79F /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */;
|
||||
|
@ -545,6 +533,20 @@
|
|||
remoteGlobalIDString = 83B06686180D5668008E3612;
|
||||
remoteInfo = MIDI;
|
||||
};
|
||||
83B73B5F2D8FC05A00A57F08 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 8372C92327C785BD00E250C9;
|
||||
remoteInfo = minimp3;
|
||||
};
|
||||
83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 8372C92227C785BD00E250C9;
|
||||
remoteInfo = minimp3;
|
||||
};
|
||||
83BB13C120E4E38E00723731 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */;
|
||||
|
@ -660,14 +662,15 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
07DFC3930ECDF80100DA400D /* CopyFiles */ = {
|
||||
07DFC3930ECDF80100DA400D /* Copy Files */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 7;
|
||||
files = (
|
||||
ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in CopyFiles */,
|
||||
ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in Copy Files */,
|
||||
);
|
||||
name = "Copy Files";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
177FD1000B90CB570011C3B5 /* CopyFiles */ = {
|
||||
|
@ -676,8 +679,8 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */,
|
||||
8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */,
|
||||
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */,
|
||||
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */,
|
||||
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */,
|
||||
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */,
|
||||
|
@ -721,14 +724,14 @@
|
|||
83256B69286661FC0036D9C0 /* libmpg123.0.dylib in CopyFiles */,
|
||||
836EF0C927BB91E900BF35B2 /* libvorbisfile.3.dylib in CopyFiles */,
|
||||
836EF0C827BB91E600BF35B2 /* libogg.0.dylib in CopyFiles */,
|
||||
83AA7D07279EBCAF00087AA4 /* libswresample.4.dylib in CopyFiles */,
|
||||
83AA7D06279EBCAD00087AA4 /* libavutil.57.dylib in CopyFiles */,
|
||||
83AA7D05279EBCAB00087AA4 /* libavformat.59.dylib in CopyFiles */,
|
||||
83AA7D04279EBCA900087AA4 /* libavcodec.59.dylib in CopyFiles */,
|
||||
83AA7D07279EBCAF00087AA4 /* libswresample.5.dylib in CopyFiles */,
|
||||
83AA7D06279EBCAD00087AA4 /* libavutil.59.dylib in CopyFiles */,
|
||||
838A33752D06A9CE00D0D770 /* librubberband.3.dylib in CopyFiles */,
|
||||
83AA7D05279EBCAB00087AA4 /* libavformat.61.dylib in CopyFiles */,
|
||||
83AA7D04279EBCA900087AA4 /* libavcodec.61.dylib in CopyFiles */,
|
||||
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */,
|
||||
8305963C277F013200EBFAAE /* File_Extractor.framework in CopyFiles */,
|
||||
ED69CBCA25BE32E80090B90D /* MASShortcut.framework in CopyFiles */,
|
||||
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */,
|
||||
17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -773,7 +776,7 @@
|
|||
177042970B8BC53600B86321 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
|
||||
177042980B8BC53600B86321 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; };
|
||||
177042990B8BC53600B86321 /* PlaybackController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PlaybackController.h; sourceTree = "<group>"; };
|
||||
1770429A0B8BC53600B86321 /* PlaybackController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PlaybackController.m; sourceTree = "<group>"; };
|
||||
1770429A0B8BC53600B86321 /* PlaybackController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaybackController.m; sourceTree = "<group>"; };
|
||||
1778D3C80F645BF00037E7A0 /* MissingAlbumArtTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MissingAlbumArtTransformer.h; path = InfoInspector/MissingAlbumArtTransformer.h; sourceTree = "<group>"; };
|
||||
1778D3C90F645BF00037E7A0 /* MissingAlbumArtTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MissingAlbumArtTransformer.m; path = InfoInspector/MissingAlbumArtTransformer.m; sourceTree = "<group>"; };
|
||||
177EBF860B8BC2A70000BC8C /* ImageTextCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ImageTextCell.h; sourceTree = "<group>"; };
|
||||
|
@ -872,19 +875,6 @@
|
|||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
32CA4F630368D1EE00C91783 /* Cog_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cog_Prefix.pch; sourceTree = "<group>"; };
|
||||
3DDFC2472344EC6B000F1453 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
|
||||
491C55B6287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/AboutWindowController.strings; sourceTree = "<group>"; };
|
||||
491C55B7287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Equalizer.strings; sourceTree = "<group>"; };
|
||||
491C55B8287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoInspector.strings; sourceTree = "<group>"; };
|
||||
491C55B9287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
491C55BA287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/OpenURLPanel.strings; sourceTree = "<group>"; };
|
||||
491C55BB287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/FileTree.strings; sourceTree = "<group>"; };
|
||||
491C55BC287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SpectrumWindow.strings; sourceTree = "<group>"; };
|
||||
491C55BD287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SpotlightPanel.strings; sourceTree = "<group>"; };
|
||||
491C55BE287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Feedback.strings; sourceTree = "<group>"; };
|
||||
491C55BF287AA4B6007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
491C55C2287AA4B7007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
491C55C3287AA4B7007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
491C55C4287AA4B7007D96F5 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = ru; path = ru.lproj/Credits.html; sourceTree = "<group>"; };
|
||||
5604D4590D60349B004F5C5D /* SpotlightWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SpotlightWindowController.m; path = Spotlight/SpotlightWindowController.m; sourceTree = "<group>"; };
|
||||
5604D45A0D60349B004F5C5D /* SpotlightWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpotlightWindowController.h; path = Spotlight/SpotlightWindowController.h; sourceTree = "<group>"; };
|
||||
5604D4F40D60726E004F5C5D /* SpotlightPlaylistEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpotlightPlaylistEntry.h; path = Spotlight/SpotlightPlaylistEntry.h; sourceTree = "<group>"; };
|
||||
|
@ -910,15 +900,11 @@
|
|||
830C37F227B9956C00E02BB0 /* analyzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = analyzer.c; sourceTree = "<group>"; };
|
||||
8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = sidplay.xcodeproj; path = Plugins/sidplay/sidplay.xcodeproj; sourceTree = "<group>"; };
|
||||
831B99BE27C23E88005A969B /* Cog.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Cog.sdef; sourceTree = "<group>"; };
|
||||
83229C9C283B0095004626A8 /* SpectrumWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpectrumWindowController.h; sourceTree = "<group>"; };
|
||||
83229C9D283B0095004626A8 /* SpectrumWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpectrumWindowController.m; sourceTree = "<group>"; };
|
||||
83256B672866617F0036D9C0 /* libmpg123.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpg123.0.dylib; path = ThirdParty/mpg123/lib/libmpg123.0.dylib; sourceTree = "<group>"; };
|
||||
8327DB8F293C923500CD0580 /* Organya.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Organya.xcodeproj; path = Plugins/Organya/Organya.xcodeproj; sourceTree = "<group>"; };
|
||||
832923AE279FAC400048201E /* Cog.q1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Cog.q1.json; sourceTree = "<group>"; };
|
||||
832CFC4E2851AA1A002AC26F /* NSView+Visibility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSView+Visibility.m"; sourceTree = "<group>"; };
|
||||
832CFC532851AA37002AC26F /* NSView+Visibility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSView+Visibility.h"; sourceTree = "<group>"; };
|
||||
832CFC542851AA8B002AC26F /* SpectrumViewCG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpectrumViewCG.h; sourceTree = SOURCE_ROOT; };
|
||||
832CFC552851AA8B002AC26F /* SpectrumViewCG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpectrumViewCG.m; sourceTree = SOURCE_ROOT; };
|
||||
833D0C2027C4ABA00060E16A /* ScriptAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScriptAdditions.h; sourceTree = "<group>"; };
|
||||
833D0C2427C4ABB80060E16A /* ScriptAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScriptAdditions.m; sourceTree = "<group>"; };
|
||||
833F681E1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
|
@ -970,7 +956,7 @@
|
|||
8370D739277419D200245CE0 /* SQLiteStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLiteStore.h; sourceTree = "<group>"; };
|
||||
8370D73C277419F700245CE0 /* SQLiteStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLiteStore.m; sourceTree = "<group>"; };
|
||||
8370D73E2775AE1300245CE0 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MAD.xcodeproj; path = Plugins/MAD/MAD.xcodeproj; sourceTree = "<group>"; };
|
||||
8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = minimp3.xcodeproj; path = Plugins/minimp3/minimp3.xcodeproj; sourceTree = "<group>"; };
|
||||
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; };
|
||||
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SpectrumViewSK.m; path = Visualization/SpectrumViewSK.m; sourceTree = "<group>"; };
|
||||
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpectrumViewSK.h; path = Visualization/SpectrumViewSK.h; sourceTree = "<group>"; };
|
||||
|
@ -984,26 +970,22 @@
|
|||
8384912518080F2D00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
|
||||
8384913D18083E4E00E7332D /* filetype.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = filetype.icns; sourceTree = "<group>"; };
|
||||
83859520234FEB35004E9946 /* Cog.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Cog.entitlements; sourceTree = "<group>"; };
|
||||
838770F32D6F0996001455A0 /* FeedbackController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FeedbackController.h; sourceTree = "<group>"; };
|
||||
838770F42D6F0996001455A0 /* FeedbackController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FeedbackController.m; sourceTree = "<group>"; };
|
||||
838A33732D06A9B100D0D770 /* librubberband.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librubberband.3.dylib; path = ThirdParty/rubberband/lib/librubberband.3.dylib; sourceTree = "<group>"; };
|
||||
838A33792D06C14200D0D770 /* PitchSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PitchSlider.h; path = Window/PitchSlider.h; sourceTree = "<group>"; };
|
||||
838A337A2D06C14200D0D770 /* PitchSlider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = PitchSlider.m; path = Window/PitchSlider.m; sourceTree = "<group>"; };
|
||||
838A337B2D06C14200D0D770 /* TempoSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TempoSlider.h; path = Window/TempoSlider.h; sourceTree = "<group>"; };
|
||||
838A337C2D06C14200D0D770 /* TempoSlider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TempoSlider.m; path = Window/TempoSlider.m; sourceTree = "<group>"; };
|
||||
838A337F2D06CF4100D0D770 /* SpectrumViewCG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpectrumViewCG.h; path = Visualization/SpectrumViewCG.h; sourceTree = "<group>"; };
|
||||
838A33802D06CF4100D0D770 /* SpectrumViewCG.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SpectrumViewCG.m; path = Visualization/SpectrumViewCG.m; sourceTree = "<group>"; };
|
||||
838A33812D06CF4100D0D770 /* SpectrumWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpectrumWindowController.h; path = Visualization/SpectrumWindowController.h; sourceTree = "<group>"; };
|
||||
838A33822D06CF4100D0D770 /* SpectrumWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SpectrumWindowController.m; path = Visualization/SpectrumWindowController.m; sourceTree = "<group>"; };
|
||||
838A33852D06CFCA00D0D770 /* SpeedButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpeedButton.h; path = Window/SpeedButton.h; sourceTree = "<group>"; };
|
||||
838A33862D06CFCA00D0D770 /* SpeedButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SpeedButton.m; path = Window/SpeedButton.m; sourceTree = "<group>"; };
|
||||
838EE79E29A8556000CD0580 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
|
||||
838EE7A029A8556500CD0580 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
|
||||
838EE7A229A8557000CD0580 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
|
||||
838EE7A429A8557200CD0580 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
|
||||
838EE8A729A8600600CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/AboutWindowController.strings; sourceTree = "<group>"; };
|
||||
838EE8A829A8600700CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Equalizer.strings; sourceTree = "<group>"; };
|
||||
838EE8A929A8600700CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoInspector.strings; sourceTree = "<group>"; };
|
||||
838EE8AA29A8600700CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
838EE8AB29A8600700CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/OpenURLPanel.strings; sourceTree = "<group>"; };
|
||||
838EE8AC29A8600700CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/FileTree.strings; sourceTree = "<group>"; };
|
||||
838EE8AD29A8600700CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/SpectrumWindow.strings; sourceTree = "<group>"; };
|
||||
838EE8AE29A8600800CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/SpotlightPanel.strings; sourceTree = "<group>"; };
|
||||
838EE8AF29A8600800CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Feedback.strings; sourceTree = "<group>"; };
|
||||
838EE8B029A8600800CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
|
||||
838EE8B129A8600800CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
838EE8B629A8600900CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
838EE8B729A8600900CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
838EE8B829A8600900CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = tr; path = tr.lproj/Credits.html; sourceTree = "<group>"; };
|
||||
838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Cog-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
838F851D256B4E5E00C3E614 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Frameworks/Sparkle.framework; sourceTree = "<group>"; };
|
||||
83922FB6286B1AA900A0B039 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
|
||||
839614A1286ED97200D3EEDB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutWindowController.xib; sourceTree = "<group>"; };
|
||||
839614A4286ED98600D3EEDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AboutWindowController.strings; sourceTree = "<group>"; };
|
||||
|
@ -1013,7 +995,6 @@
|
|||
839614AC286EDA5C00D3EEDB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SpectrumWindow.xib; sourceTree = "<group>"; };
|
||||
839614AF286EDA6800D3EEDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SpectrumWindow.strings; sourceTree = "<group>"; };
|
||||
839614B1286EDA6E00D3EEDB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SpectrumWindow.strings; sourceTree = "<group>"; };
|
||||
83978E28285C5C0A0076ED21 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
83988F0C27BE0A5900A0E89A /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RedundantPlaylistDataStore.h; sourceTree = "<group>"; };
|
||||
83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RedundantPlaylistDataStore.m; sourceTree = "<group>"; };
|
||||
8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = "<group>"; };
|
||||
|
@ -1023,12 +1004,14 @@
|
|||
839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Merge.m"; sourceTree = "<group>"; };
|
||||
839E3B53286595D700880EA2 /* GeneralPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GeneralPane.h; path = Preferences/Preferences/GeneralPane.h; sourceTree = "<group>"; };
|
||||
839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SADIE_D02-96000.mhr"; sourceTree = "<group>"; };
|
||||
839E876C2D5DA0AC00A13526 /* RubberbandEngineTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RubberbandEngineTransformer.h; path = Preferences/Preferences/RubberbandEngineTransformer.h; sourceTree = "<group>"; };
|
||||
839E876D2D5DA0AC00A13526 /* RubberbandEngineTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = RubberbandEngineTransformer.m; path = Preferences/Preferences/RubberbandEngineTransformer.m; sourceTree = "<group>"; };
|
||||
83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ColorToValueTransformer.m; path = Preferences/Preferences/ColorToValueTransformer.m; sourceTree = "<group>"; };
|
||||
83A3B733283AE6AA00CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ColorToValueTransformer.h; path = Preferences/Preferences/ColorToValueTransformer.h; sourceTree = "<group>"; };
|
||||
83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.59.dylib; path = ThirdParty/ffmpeg/lib/libavcodec.59.dylib; sourceTree = "<group>"; };
|
||||
83AA7D01279EBC8200087AA4 /* libswresample.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libswresample.4.dylib; path = ThirdParty/ffmpeg/lib/libswresample.4.dylib; sourceTree = "<group>"; };
|
||||
83AA7D02279EBC8200087AA4 /* libavutil.57.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavutil.57.dylib; path = ThirdParty/ffmpeg/lib/libavutil.57.dylib; sourceTree = "<group>"; };
|
||||
83AA7D03279EBC8300087AA4 /* libavformat.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavformat.59.dylib; path = ThirdParty/ffmpeg/lib/libavformat.59.dylib; sourceTree = "<group>"; };
|
||||
83AA7D00279EBC8200087AA4 /* libavcodec.61.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.61.dylib; path = ThirdParty/ffmpeg/lib/libavcodec.61.dylib; sourceTree = "<group>"; };
|
||||
83AA7D01279EBC8200087AA4 /* libswresample.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libswresample.5.dylib; path = ThirdParty/ffmpeg/lib/libswresample.5.dylib; sourceTree = "<group>"; };
|
||||
83AA7D02279EBC8200087AA4 /* libavutil.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavutil.59.dylib; path = ThirdParty/ffmpeg/lib/libavutil.59.dylib; sourceTree = "<group>"; };
|
||||
83AA7D03279EBC8300087AA4 /* libavformat.61.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavformat.61.dylib; path = ThirdParty/ffmpeg/lib/libavformat.61.dylib; sourceTree = "<group>"; };
|
||||
83AB9031237CEFD300A433D5 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
|
||||
83B0669C180D5668008E3612 /* MIDI.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MIDI.xcodeproj; path = Plugins/MIDI/MIDI.xcodeproj; sourceTree = "<group>"; };
|
||||
83B61E2329A8296500CD0580 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LyricsWindow.xib; sourceTree = "<group>"; };
|
||||
|
@ -1055,24 +1038,11 @@
|
|||
83BC5AD820E4D0D900631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SpotlightPanel.strings; sourceTree = "<group>"; };
|
||||
83BC5ADA20E4D0E900631CD4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Feedback.strings; sourceTree = "<group>"; };
|
||||
83BC5ADC20E4D0EC00631CD4 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Feedback.strings; sourceTree = "<group>"; };
|
||||
83CB565C2E064607003DD379 /* NSDictionary+Optional.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Optional.h"; sourceTree = "<group>"; };
|
||||
83CB565D2E064607003DD379 /* NSDictionary+Optional.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Optional.m"; sourceTree = "<group>"; };
|
||||
83D0380E24A40DF2004CF90F /* CogAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = CogAssets.xcassets; sourceTree = "<group>"; };
|
||||
83D2E23D287ED5ED00DD441F /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pl; path = pl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AdPlug.xcodeproj; path = Plugins/AdPlug/AdPlug.xcodeproj; sourceTree = "<group>"; };
|
||||
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPT.xcodeproj; path = Plugins/OpenMPT/OpenMPT.xcodeproj; sourceTree = "<group>"; };
|
||||
83F0E6A3287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/AboutWindowController.strings; sourceTree = "<group>"; };
|
||||
83F0E6A4287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Equalizer.strings; sourceTree = "<group>"; };
|
||||
83F0E6A5287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoInspector.strings; sourceTree = "<group>"; };
|
||||
83F0E6A6287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
83F0E6A7287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/OpenURLPanel.strings; sourceTree = "<group>"; };
|
||||
83F0E6A8287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/FileTree.strings; sourceTree = "<group>"; };
|
||||
83F0E6A9287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/SpectrumWindow.strings; sourceTree = "<group>"; };
|
||||
83F0E6AA287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/SpotlightPanel.strings; sourceTree = "<group>"; };
|
||||
83F0E6AB287CAB3800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Feedback.strings; sourceTree = "<group>"; };
|
||||
83F0E8AD287CD48800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
83F0E8B1287CD50700D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
83F0E8B2287CD52500D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = pl; path = pl.lproj/Credits.html; sourceTree = "<group>"; };
|
||||
83F7AAD7290B682400951B61 /* IIAM.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = IIAM.bin; sourceTree = "<group>"; };
|
||||
83F7AAD8290B682400951B61 /* scpipe */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = scpipe; sourceTree = "<group>"; };
|
||||
83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SilenceDecoder.xcodeproj; path = Plugins/SilenceDecoder/SilenceDecoder.xcodeproj; sourceTree = "<group>"; };
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8E07AB760AAC930B00A4B32F /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PreferencesController.h; path = Preferences/PreferencesController.h; sourceTree = "<group>"; };
|
||||
|
@ -1114,12 +1084,11 @@
|
|||
83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */,
|
||||
835FAC7E27BCDF5B00BA8562 /* libaom.a in Frameworks */,
|
||||
837DC92B285B05710005C58A /* CoreData.framework in Frameworks */,
|
||||
83978E26285C596F0076ED21 /* FirebaseAnalytics in Frameworks */,
|
||||
838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */,
|
||||
17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */,
|
||||
17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */,
|
||||
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */,
|
||||
83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */,
|
||||
17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */,
|
||||
83978E16285C58190076ED21 /* FirebaseCrashlytics in Frameworks */,
|
||||
17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1139,6 +1108,7 @@
|
|||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
838770F72D6F0996001455A0 /* Feedback */,
|
||||
835F00B3279BD1CD00055FCF /* Formatters */,
|
||||
177042960B8BC53600B86321 /* Application */,
|
||||
17E0D5D20F520E75005B6FED /* Window */,
|
||||
|
@ -1162,7 +1132,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */,
|
||||
838F851D256B4E5E00C3E614 /* Sparkle.framework */,
|
||||
17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */,
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
||||
);
|
||||
|
@ -1172,6 +1141,7 @@
|
|||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
838A33732D06A9B100D0D770 /* librubberband.3.dylib */,
|
||||
836DF616298F6EC400CD0580 /* libsoxr.0.dylib */,
|
||||
83256B672866617F0036D9C0 /* libmpg123.0.dylib */,
|
||||
835FAC7C27BCDF5B00BA8562 /* libaom.a */,
|
||||
|
@ -1183,10 +1153,10 @@
|
|||
836EF0C627BB91AB00BF35B2 /* libvorbisfile.3.dylib */,
|
||||
836EF0C427BB919300BF35B2 /* libogg.0.dylib */,
|
||||
83B72E2A279044F6006007A3 /* libfdk-aac.2.dylib */,
|
||||
83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */,
|
||||
83AA7D03279EBC8300087AA4 /* libavformat.59.dylib */,
|
||||
83AA7D02279EBC8200087AA4 /* libavutil.57.dylib */,
|
||||
83AA7D01279EBC8200087AA4 /* libswresample.4.dylib */,
|
||||
83AA7D00279EBC8200087AA4 /* libavcodec.61.dylib */,
|
||||
83AA7D03279EBC8300087AA4 /* libavformat.61.dylib */,
|
||||
83AA7D02279EBC8200087AA4 /* libavutil.59.dylib */,
|
||||
83AA7D01279EBC8200087AA4 /* libswresample.5.dylib */,
|
||||
83059634277F011100EBFAAE /* File_Extractor.xcodeproj */,
|
||||
17BB5EA50B8A87850009ACB1 /* IOKit.framework */,
|
||||
17BB5CF60B8A86350009ACB1 /* AudioUnit.framework */,
|
||||
|
@ -1241,6 +1211,8 @@
|
|||
177EC0110B8BC2CF0000BC8C /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83CB565C2E064607003DD379 /* NSDictionary+Optional.h */,
|
||||
83CB565D2E064607003DD379 /* NSDictionary+Optional.m */,
|
||||
834A42C4287B01B600EB9D9B /* AudioChunk.h */,
|
||||
8384912518080F2D00E7332D /* Logging.h */,
|
||||
07E18DF10D62B38400BB0E11 /* NSArray+ShuffleUtils.h */,
|
||||
|
@ -1303,7 +1275,7 @@
|
|||
17C808830C3BD181005707C4 /* HTTPSource.xcodeproj */,
|
||||
83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */,
|
||||
8E8D40820CBB036600135C1B /* M3u.xcodeproj */,
|
||||
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */,
|
||||
8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */,
|
||||
83B0669C180D5668008E3612 /* MIDI.xcodeproj */,
|
||||
17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */,
|
||||
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */,
|
||||
|
@ -1406,6 +1378,8 @@
|
|||
17E0D5D20F520E75005B6FED /* Window */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0A9CEA012861501700E47168 /* AboutWindowController.swift */,
|
||||
0A9CEA0A286152DF00E47168 /* DraggableView.swift */,
|
||||
83BC5AB120E4C87100631CD4 /* DualWindow.h */,
|
||||
83BC5AB020E4C87100631CD4 /* DualWindow.m */,
|
||||
17E0D5E10F520F02005B6FED /* MainWindow.h */,
|
||||
|
@ -1414,6 +1388,8 @@
|
|||
836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */,
|
||||
17E0D5E30F520F02005B6FED /* MiniWindow.h */,
|
||||
17E0D5E40F520F02005B6FED /* MiniWindow.m */,
|
||||
838A33792D06C14200D0D770 /* PitchSlider.h */,
|
||||
838A337A2D06C14200D0D770 /* PitchSlider.m */,
|
||||
1752C36A0F59E00100F85F28 /* PlaybackButtons.h */,
|
||||
1752C36B0F59E00100F85F28 /* PlaybackButtons.m */,
|
||||
17E0D5E50F520F02005B6FED /* PositionSlider.h */,
|
||||
|
@ -1423,14 +1399,16 @@
|
|||
172A12320F5911D20078EF0C /* RepeatTransformers.m */,
|
||||
172A123A0F5912AE0078EF0C /* ShuffleTransformers.h */,
|
||||
172A123B0F5912AE0078EF0C /* ShuffleTransformers.m */,
|
||||
838A33852D06CFCA00D0D770 /* SpeedButton.h */,
|
||||
838A33862D06CFCA00D0D770 /* SpeedButton.m */,
|
||||
838A337B2D06C14200D0D770 /* TempoSlider.h */,
|
||||
838A337C2D06C14200D0D770 /* TempoSlider.m */,
|
||||
17E0D5E70F520F02005B6FED /* TimeField.h */,
|
||||
17E0D5E80F520F02005B6FED /* TimeField.m */,
|
||||
17E0D6180F520F9F005B6FED /* VolumeButton.h */,
|
||||
17E0D6190F520F9F005B6FED /* VolumeButton.m */,
|
||||
17E0D61A0F520F9F005B6FED /* VolumeSlider.h */,
|
||||
17E0D61B0F520F9F005B6FED /* VolumeSlider.m */,
|
||||
0A9CEA012861501700E47168 /* AboutWindowController.swift */,
|
||||
0A9CEA0A286152DF00E47168 /* DraggableView.swift */,
|
||||
);
|
||||
name = Window;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1438,6 +1416,8 @@
|
|||
17E0D5F60F520F42005B6FED /* Transformers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
839E876C2D5DA0AC00A13526 /* RubberbandEngineTransformer.h */,
|
||||
839E876D2D5DA0AC00A13526 /* RubberbandEngineTransformer.m */,
|
||||
83A3B733283AE6AA00CC6593 /* ColorToValueTransformer.h */,
|
||||
83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */,
|
||||
17E0D6120F520F87005B6FED /* FontSizetoLineHeightTransformer.h */,
|
||||
|
@ -1488,7 +1468,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0A1B412E286F6301008A6A44 /* Localizable.stringsdict */,
|
||||
83978E28285C5C0A0076ED21 /* GoogleService-Info.plist */,
|
||||
83D0380E24A40DF2004CF90F /* CogAssets.xcassets */,
|
||||
83859520234FEB35004E9946 /* Cog.entitlements */,
|
||||
080E96DDFE201D6D7F000001 /* Classes */,
|
||||
|
@ -1514,8 +1493,6 @@
|
|||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83F7AAD7290B682400951B61 /* IIAM.bin */,
|
||||
83F7AAD8290B682400951B61 /* scpipe */,
|
||||
839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */,
|
||||
837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */,
|
||||
8E07AD280AAC9BE600A4B32F /* Preference Panes */,
|
||||
|
@ -1801,31 +1778,32 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8372C92B27C785BD00E250C9 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8372C93027C785BE00E250C9 /* MAD.bundle */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8377C66027B8CF2300E8BC0F /* Visualization */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830C37EF27B9956C00E02BB0 /* ThirdParty */,
|
||||
8377C66427B8CF7A00E8BC0F /* VisualizationController.h */,
|
||||
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */,
|
||||
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */,
|
||||
832CFC542851AA8B002AC26F /* SpectrumViewCG.h */,
|
||||
832CFC552851AA8B002AC26F /* SpectrumViewCG.m */,
|
||||
8377C6B727B900F000E8BC0F /* SpectrumItem.h */,
|
||||
8377C6B827B900F000E8BC0F /* SpectrumItem.m */,
|
||||
83229C9C283B0095004626A8 /* SpectrumWindowController.h */,
|
||||
83229C9D283B0095004626A8 /* SpectrumWindowController.m */,
|
||||
838A337F2D06CF4100D0D770 /* SpectrumViewCG.h */,
|
||||
838A33802D06CF4100D0D770 /* SpectrumViewCG.m */,
|
||||
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */,
|
||||
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */,
|
||||
838A33812D06CF4100D0D770 /* SpectrumWindowController.h */,
|
||||
838A33822D06CF4100D0D770 /* SpectrumWindowController.m */,
|
||||
8377C66427B8CF7A00E8BC0F /* VisualizationController.h */,
|
||||
);
|
||||
name = Visualization;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
838770F72D6F0996001455A0 /* Feedback */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
838770F32D6F0996001455A0 /* FeedbackController.h */,
|
||||
838770F42D6F0996001455A0 /* FeedbackController.m */,
|
||||
);
|
||||
path = Feedback;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83B0669D180D5668008E3612 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1843,6 +1821,14 @@
|
|||
path = LyricsWindow;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83B73B5C2D8FC05A00A57F08 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83B73B602D8FC05A00A57F08 /* minimp3.bundle */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83BB13AE20E4E38E00723731 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1997,14 +1983,14 @@
|
|||
8D1107290486CEB800E47090 /* Resources */,
|
||||
8E757AEC09F3265E0080F1EE /* CopyFiles */,
|
||||
177FD1000B90CB570011C3B5 /* CopyFiles */,
|
||||
07DFC3930ECDF80100DA400D /* CopyFiles */,
|
||||
83978E27285C5A4C0076ED21 /* Run Crashlytics symbol upload */,
|
||||
07DFC3930ECDF80100DA400D /* Copy Files */,
|
||||
83978E27285C5A4C0076ED21 /* Run Sentry symbol upload */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
83B73B642D8FD74000A57F08 /* PBXTargetDependency */,
|
||||
8327DBA8293CAD0A00CD0580 /* PBXTargetDependency */,
|
||||
8372C93C27C7893100E250C9 /* PBXTargetDependency */,
|
||||
83489C6A2782F76900BDCEA2 /* PBXTargetDependency */,
|
||||
ED69CBC625BE32B40090B90D /* PBXTargetDependency */,
|
||||
834D793E20E4EFD200C4A5CC /* PBXTargetDependency */,
|
||||
|
@ -2037,8 +2023,7 @@
|
|||
);
|
||||
name = Cog;
|
||||
packageProductDependencies = (
|
||||
83978E15285C58190076ED21 /* FirebaseCrashlytics */,
|
||||
83978E25285C596F0076ED21 /* FirebaseAnalytics */,
|
||||
83F9FFEE2D6EB75B00026576 /* Sentry */,
|
||||
);
|
||||
productInstallPath = /Applications;
|
||||
productName = Cog;
|
||||
|
@ -2053,16 +2038,9 @@
|
|||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1500;
|
||||
TargetAttributes = {
|
||||
8D1107260486CEB800E47090 = {
|
||||
DevelopmentTeam = "";
|
||||
LastSwiftMigration = 1220;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Cog" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
|
@ -2082,7 +2060,7 @@
|
|||
);
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* Cog */;
|
||||
packageReferences = (
|
||||
83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
|
||||
83F9FFED2D6EB75B00026576 /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
|
||||
);
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
|
@ -2150,10 +2128,6 @@
|
|||
ProductGroup = 8E8D40830CBB036600135C1B /* Products */;
|
||||
ProjectRef = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 8372C92B27C785BD00E250C9 /* Products */;
|
||||
ProjectRef = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = ED69CBB925BE328C0090B90D /* Products */;
|
||||
ProjectRef = ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */;
|
||||
|
@ -2162,6 +2136,10 @@
|
|||
ProductGroup = 83B0669D180D5668008E3612 /* Products */;
|
||||
ProjectRef = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 83B73B5C2D8FC05A00A57F08 /* Products */;
|
||||
ProjectRef = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 17C8089F0C3BD1AB005707C4 /* Products */;
|
||||
ProjectRef = 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */;
|
||||
|
@ -2377,13 +2355,6 @@
|
|||
remoteRef = 836FB5461820538800B3AD2D /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
8372C93027C785BE00E250C9 /* MAD.bundle */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = MAD.bundle;
|
||||
remoteRef = 8372C92F27C785BE00E250C9 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
83B066A1180D5669008E3612 /* MIDI.bundle */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
|
@ -2391,6 +2362,13 @@
|
|||
remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
83B73B602D8FC05A00A57F08 /* minimp3.bundle */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = minimp3.bundle;
|
||||
remoteRef = 83B73B5F2D8FC05A00A57F08 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
83BB13C220E4E38E00723731 /* vgmstream.bundle */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
|
@ -2501,13 +2479,10 @@
|
|||
17D1B27F0CF8B2830028F5B5 /* pls.icns in Resources */,
|
||||
17D1B2800CF8B2830028F5B5 /* s3m.icns in Resources */,
|
||||
17D1B2810CF8B2830028F5B5 /* song.icns in Resources */,
|
||||
83F7AADA290B682400951B61 /* scpipe in Resources */,
|
||||
83F7AADE290B8DDF00951B61 /* IIAM.bin in Resources */,
|
||||
831B99BF27C23E88005A969B /* Cog.sdef in Resources */,
|
||||
832923AF279FAC400048201E /* Cog.q1.json in Resources */,
|
||||
17D1B2820CF8B2830028F5B5 /* vg.icns in Resources */,
|
||||
17D1B2830CF8B2830028F5B5 /* xm.icns in Resources */,
|
||||
83978E29285C5C0A0076ED21 /* GoogleService-Info.plist in Resources */,
|
||||
836DF61E298F7F6E00CD0580 /* Scenes.scnassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -2534,7 +2509,7 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "$SCRIPT_INPUT_FILE_0 $SCRIPT_INPUT_FILE_1 $SCRIPT_OUTPUT_FILE_0\n";
|
||||
};
|
||||
83978E27285C5A4C0076ED21 /* Run Crashlytics symbol upload */ = {
|
||||
83978E27285C5A4C0076ED21 /* Run Sentry symbol upload */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 12;
|
||||
|
@ -2546,14 +2521,14 @@
|
|||
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}",
|
||||
"$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
|
||||
);
|
||||
name = "Run Crashlytics symbol upload";
|
||||
name = "Run Sentry symbol upload";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [[ \"${CONFIGURATION}\" = \"Release\" ]] || [[ \"${CONFIGURATION}\" = \"Adhoc\" ]]; then\n echo \"Uploading dSYMs...\"\n find \"${DWARF_DSYM_FOLDER_PATH}\" -name \"*.dSYM\" -print0 | xargs -0 -I \\{\\} \"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/upload-symbols\" -gsp \"${SRCROOT}/GoogleService-Info.plist\" -p mac \\{\\}\nelse\n echo \"Skip dSYMs upload\"\nfi\n\nexit 0\n";
|
||||
shellScript = "if [[ \"x${SENTRY_URL}\" != \"x\" ]] && [[ \"x${SENTRY_PROJECT}\" != \"x\" ]] && [[ \"x${SENTRY_AUTH_TOKEN}\" != \"x\" ]]; then\n if [[ \"${CONFIGURATION}\" = \"Release\" ]] || [[ \"${CONFIGURATION}\" = \"Adhoc\" ]]; then\n echo \"Uploading dSYMs...\"\n export PATH=$PATH:/usr/local/bin:/opt/homebrew/bin\n sentry-cli --url \"${SENTRY_URL}\" debug-files upload -p \"${SENTRY_PROJECT}\" --auth-token \"${SENTRY_AUTH_TOKEN}\" --include-sources \"${DWARF_DSYM_FOLDER_PATH}\"\n else\n echo \"Skip dSYMs upload\"\n fi\nelse\n echo \"Sentry settings not configured\"\nfi\n\nexit 0\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
@ -2574,6 +2549,7 @@
|
|||
177EBFA70B8BC2A70000BC8C /* ImageTextCell.m in Sources */,
|
||||
177EC0270B8BC2CF0000BC8C /* TrackingCell.m in Sources */,
|
||||
177EC0290B8BC2CF0000BC8C /* TrackingSlider.m in Sources */,
|
||||
838A33872D06CFCA00D0D770 /* SpeedButton.m in Sources */,
|
||||
1770429C0B8BC53600B86321 /* AppController.m in Sources */,
|
||||
1770429E0B8BC53600B86321 /* PlaybackController.m in Sources */,
|
||||
8355D6B6180612F300D05687 /* NSData+MD5.m in Sources */,
|
||||
|
@ -2581,6 +2557,7 @@
|
|||
1755E1F90BA0D2B600CA3560 /* PlaylistLoader.m in Sources */,
|
||||
8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */,
|
||||
179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */,
|
||||
838770FA2D6F0996001455A0 /* FeedbackController.m in Sources */,
|
||||
837DC931285B3F790005C58A /* DataModel.xcdatamodeld in Sources */,
|
||||
835FAC7927BCDF2A00BA8562 /* AVIFDecoder.m in Sources */,
|
||||
EDAAA41F25A665C000731773 /* PositionSliderToolbarItem.swift in Sources */,
|
||||
|
@ -2590,8 +2567,13 @@
|
|||
5604D4F60D60726E004F5C5D /* SpotlightPlaylistEntry.m in Sources */,
|
||||
56462EAF0D6341F6000AB68C /* SpotlightTransformers.m in Sources */,
|
||||
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */,
|
||||
832CFC562851AA8B002AC26F /* SpectrumViewCG.m in Sources */,
|
||||
838A337D2D06C14200D0D770 /* TempoSlider.m in Sources */,
|
||||
838A337E2D06C14200D0D770 /* PitchSlider.m in Sources */,
|
||||
838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */,
|
||||
839E876E2D5DA0AC00A13526 /* RubberbandEngineTransformer.m in Sources */,
|
||||
838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */,
|
||||
83B61E2829A82A0200CD0580 /* LyricsWindowController.m in Sources */,
|
||||
83CB565E2E064607003DD379 /* NSDictionary+Optional.m in Sources */,
|
||||
56462EB20D634206000AB68C /* SpotlightPlaylistController.m in Sources */,
|
||||
07E18DF30D62B38400BB0E11 /* NSArray+ShuffleUtils.m in Sources */,
|
||||
56C63D910D647DF300EAE25A /* NSComparisonPredicate+CogPredicate.m in Sources */,
|
||||
|
@ -2637,7 +2619,6 @@
|
|||
17F6C8070F603701000D9DA9 /* PlaybackEventController.m in Sources */,
|
||||
83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */,
|
||||
8307D30E28606148000FF8EB /* SandboxBroker.m in Sources */,
|
||||
83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */,
|
||||
835F00BB279BD1CD00055FCF /* SecondsFormatter.m in Sources */,
|
||||
1784560F0F631E24007E8021 /* FileTreeViewController.m in Sources */,
|
||||
178456120F631E31007E8021 /* SideViewController.m in Sources */,
|
||||
|
@ -2766,11 +2747,6 @@
|
|||
name = Hively;
|
||||
targetProxy = 836FB5A518206F1500B3AD2D /* PBXContainerItemProxy */;
|
||||
};
|
||||
8372C93C27C7893100E250C9 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = MAD;
|
||||
targetProxy = 8372C93B27C7893100E250C9 /* PBXContainerItemProxy */;
|
||||
};
|
||||
8375B36217FFEF010092A79F /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = ArchiveSource;
|
||||
|
@ -2781,6 +2757,11 @@
|
|||
name = MIDI;
|
||||
targetProxy = 83B06702180D5776008E3612 /* PBXContainerItemProxy */;
|
||||
};
|
||||
83B73B642D8FD74000A57F08 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = minimp3;
|
||||
targetProxy = 83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */;
|
||||
};
|
||||
83BCB8D917FC96F800760340 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = HighlyComplete;
|
||||
|
@ -2819,9 +2800,6 @@
|
|||
children = (
|
||||
833F681E1CDBCAA700AFB9F0 /* es */,
|
||||
835C888C22CC1882001B4B3F /* en */,
|
||||
83F0E8AD287CD48800D84594 /* pl */,
|
||||
491C55C2287AA4B7007D96F5 /* ru */,
|
||||
838EE8B629A8600900CD0580 /* tr */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2831,9 +2809,6 @@
|
|||
children = (
|
||||
0A1B412D286F6301008A6A44 /* es */,
|
||||
0A1B412F286F6307008A6A44 /* en */,
|
||||
491C55BF287AA4B6007D96F5 /* ru */,
|
||||
83D2E23D287ED5ED00DD441F /* pl */,
|
||||
838EE8B129A8600800CD0580 /* tr */,
|
||||
);
|
||||
name = Localizable.stringsdict;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2844,9 +2819,6 @@
|
|||
83BC5AB720E4C91400631CD4 /* Base */,
|
||||
83BC5AD220E4D0B400631CD4 /* es */,
|
||||
83BC5AD420E4D0B600631CD4 /* en */,
|
||||
83F0E6A8287CAB3800D84594 /* pl */,
|
||||
491C55BB287AA4B6007D96F5 /* ru */,
|
||||
838EE8AC29A8600700CD0580 /* tr */,
|
||||
);
|
||||
name = FileTree.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2857,9 +2829,6 @@
|
|||
83BC5AB520E4C91200631CD4 /* Base */,
|
||||
83BC5AC620E4D04600631CD4 /* en */,
|
||||
83BC5AC820E4D05A00631CD4 /* es */,
|
||||
83F0E6A6287CAB3800D84594 /* pl */,
|
||||
491C55B9287AA4B6007D96F5 /* ru */,
|
||||
838EE8AA29A8600700CD0580 /* tr */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2870,9 +2839,6 @@
|
|||
83BC5AB620E4C91300631CD4 /* Base */,
|
||||
83BC5ACE20E4D09700631CD4 /* es */,
|
||||
83BC5AD020E4D09800631CD4 /* en */,
|
||||
83F0E6A7287CAB3800D84594 /* pl */,
|
||||
491C55BA287AA4B6007D96F5 /* ru */,
|
||||
838EE8AB29A8600700CD0580 /* tr */,
|
||||
);
|
||||
name = OpenURLPanel.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2883,9 +2849,6 @@
|
|||
83BC5AB820E4C91400631CD4 /* Base */,
|
||||
83BC5AD620E4D0D800631CD4 /* es */,
|
||||
83BC5AD820E4D0D900631CD4 /* en */,
|
||||
83F0E6AA287CAB3800D84594 /* pl */,
|
||||
491C55BD287AA4B6007D96F5 /* ru */,
|
||||
838EE8AE29A8600800CD0580 /* tr */,
|
||||
);
|
||||
name = SpotlightPanel.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2896,9 +2859,6 @@
|
|||
83BC5AB420E4C91100631CD4 /* Base */,
|
||||
83BC5ACA20E4D07200631CD4 /* es */,
|
||||
83BC5ACC20E4D07700631CD4 /* en */,
|
||||
83F0E6A5287CAB3800D84594 /* pl */,
|
||||
491C55B8287AA4B6007D96F5 /* ru */,
|
||||
838EE8A929A8600700CD0580 /* tr */,
|
||||
);
|
||||
name = InfoInspector.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2909,9 +2869,6 @@
|
|||
83BC5AB920E4C91500631CD4 /* Base */,
|
||||
83BC5ADA20E4D0E900631CD4 /* en */,
|
||||
83BC5ADC20E4D0EC00631CD4 /* es */,
|
||||
83F0E6AB287CAB3800D84594 /* pl */,
|
||||
491C55BE287AA4B6007D96F5 /* ru */,
|
||||
838EE8AF29A8600800CD0580 /* tr */,
|
||||
);
|
||||
name = Feedback.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2921,9 +2878,6 @@
|
|||
children = (
|
||||
833F68251CDBCAA800AFB9F0 /* es */,
|
||||
835C888B22CC1881001B4B3F /* en */,
|
||||
83F0E8B2287CD52500D84594 /* pl */,
|
||||
491C55C4287AA4B7007D96F5 /* ru */,
|
||||
838EE8B829A8600900CD0580 /* tr */,
|
||||
);
|
||||
name = Credits.html;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2934,9 +2888,6 @@
|
|||
830C37A027B95E3000E02BB0 /* Base */,
|
||||
839614A8286EDA0400D3EEDB /* en */,
|
||||
839614AA286EDA0600D3EEDB /* es */,
|
||||
83F0E6A4287CAB3800D84594 /* pl */,
|
||||
491C55B7287AA4B6007D96F5 /* ru */,
|
||||
838EE8A829A8600700CD0580 /* tr */,
|
||||
);
|
||||
name = Equalizer.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2947,9 +2898,6 @@
|
|||
839614A1286ED97200D3EEDB /* Base */,
|
||||
839614A4286ED98600D3EEDB /* en */,
|
||||
839614A6286ED98800D3EEDB /* es */,
|
||||
83F0E6A3287CAB3800D84594 /* pl */,
|
||||
491C55B6287AA4B6007D96F5 /* ru */,
|
||||
838EE8A729A8600600CD0580 /* tr */,
|
||||
);
|
||||
name = AboutWindowController.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2960,9 +2908,6 @@
|
|||
839614AC286EDA5C00D3EEDB /* Base */,
|
||||
839614AF286EDA6800D3EEDB /* en */,
|
||||
839614B1286EDA6E00D3EEDB /* es */,
|
||||
83F0E6A9287CAB3800D84594 /* pl */,
|
||||
491C55BC287AA4B6007D96F5 /* ru */,
|
||||
838EE8AD29A8600700CD0580 /* tr */,
|
||||
);
|
||||
name = SpectrumWindow.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2973,9 +2918,6 @@
|
|||
83B61E2329A8296500CD0580 /* Base */,
|
||||
838EE79E29A8556000CD0580 /* en */,
|
||||
838EE7A029A8556500CD0580 /* es */,
|
||||
838EE7A229A8557000CD0580 /* ru */,
|
||||
838EE7A429A8557200CD0580 /* pl */,
|
||||
838EE8B029A8600800CD0580 /* tr */,
|
||||
);
|
||||
name = LyricsWindow.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2985,9 +2927,6 @@
|
|||
children = (
|
||||
833F681F1CDBCAA800AFB9F0 /* es */,
|
||||
835C888D22CC1882001B4B3F /* en */,
|
||||
83F0E8B1287CD50700D84594 /* pl */,
|
||||
491C55C3287AA4B7007D96F5 /* ru */,
|
||||
838EE8B729A8600900CD0580 /* tr */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3000,18 +2939,15 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "Cog color";
|
||||
CODE_SIGN_ENTITLEMENTS = Cog.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/ThirdParty/Frameworks";
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
HEADER_SEARCH_PATHS = ThirdParty/avif/include;
|
||||
IBC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
|
||||
IBSC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Cog;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
|
||||
|
@ -3026,6 +2962,8 @@
|
|||
"$(PROJECT_DIR)/ThirdParty/avif/lib",
|
||||
"$(PROJECT_DIR)/ThirdParty/mpg123/lib",
|
||||
"$(PROJECT_DIR)/ThirdParty/soxr/lib",
|
||||
"$(PROJECT_DIR)",
|
||||
"$(PROJECT_DIR)/ThirdParty/rubberband/lib",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-D__MACOSX__",
|
||||
|
@ -3045,7 +2983,6 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cog;
|
||||
PRODUCT_NAME = Cog;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Cog-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
@ -3059,15 +2996,12 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = "Cog color";
|
||||
CODE_SIGN_ENTITLEMENTS = Cog.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/ThirdParty/Frameworks";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
HEADER_SEARCH_PATHS = ThirdParty/avif/include;
|
||||
IBC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
|
||||
IBSC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Cog;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
|
||||
|
@ -3082,6 +3016,8 @@
|
|||
"$(PROJECT_DIR)/ThirdParty/avif/lib",
|
||||
"$(PROJECT_DIR)/ThirdParty/mpg123/lib",
|
||||
"$(PROJECT_DIR)/ThirdParty/soxr/lib",
|
||||
"$(PROJECT_DIR)",
|
||||
"$(PROJECT_DIR)/ThirdParty/rubberband/lib",
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-D__MACOSX__",
|
||||
|
@ -3101,7 +3037,6 @@
|
|||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cog;
|
||||
PRODUCT_NAME = Cog;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Cog-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -3136,8 +3071,9 @@
|
|||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Cog.entitlements;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -3148,7 +3084,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
|
@ -3181,8 +3117,9 @@
|
|||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Cog.entitlements;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -3191,7 +3128,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
};
|
||||
|
@ -3221,26 +3158,21 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
|
||||
83F9FFED2D6EB75B00026576 /* XCRemoteSwiftPackageReference "sentry-cocoa" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
|
||||
repositoryURL = "https://github.com/getsentry/sentry-cocoa.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 9.0.0;
|
||||
minimumVersion = 8.47.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
83978E15285C58190076ED21 /* FirebaseCrashlytics */ = {
|
||||
83F9FFEE2D6EB75B00026576 /* Sentry */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
|
||||
productName = FirebaseCrashlytics;
|
||||
};
|
||||
83978E25285C596F0076ED21 /* FirebaseAnalytics */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 83978E14285C58190076ED21 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
|
||||
productName = FirebaseAnalytics;
|
||||
package = 83F9FFED2D6EB75B00026576 /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
|
||||
productName = Sentry;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
|
|
|
@ -1,113 +1,15 @@
|
|||
{
|
||||
"originHash" : "7b5e54f81ac1ebaa640945691cb38c371b637198701f04fba811702fc8e7067e",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "abseil-cpp-swiftpm",
|
||||
"identity" : "sentry-cocoa",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git",
|
||||
"location" : "https://github.com/getsentry/sentry-cocoa.git",
|
||||
"state" : {
|
||||
"revision" : "d302de612e3d57c6f4afaf087da18fba8eac72a7",
|
||||
"version" : "0.20220203.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "boringssl-swiftpm",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/boringssl-SwiftPM.git",
|
||||
"state" : {
|
||||
"revision" : "79db6516894a932d0ddaff3b05b9da1e4f6c4069",
|
||||
"version" : "0.9.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "firebase-ios-sdk",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/firebase-ios-sdk",
|
||||
"state" : {
|
||||
"revision" : "2eb177effe7baf1f13ad0c5f4eb8c71a98429fb5",
|
||||
"version" : "9.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleappmeasurement",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleAppMeasurement.git",
|
||||
"state" : {
|
||||
"revision" : "192cce3e0486aecfdb61102a9c694c78dc89dc48",
|
||||
"version" : "9.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googledatatransport",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleDataTransport.git",
|
||||
"state" : {
|
||||
"revision" : "b905c49326b72211531ed9d7baa02d724828a8dc",
|
||||
"version" : "9.1.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "googleutilities",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/GoogleUtilities.git",
|
||||
"state" : {
|
||||
"revision" : "f4abe56ce62a779e64b525eb133c8fc2a84bbc1f",
|
||||
"version" : "7.7.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-ios",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-ios.git",
|
||||
"state" : {
|
||||
"revision" : "2af4f6e9c2b18beae228f50b1198c641be859d2b",
|
||||
"version" : "1.44.2-grpc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "gtm-session-fetcher",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/gtm-session-fetcher.git",
|
||||
"state" : {
|
||||
"revision" : "19605024d59eaefdb1f6a2cb11ebe75df4421126",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "leveldb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/leveldb.git",
|
||||
"state" : {
|
||||
"revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
|
||||
"version" : "1.22.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "nanopb",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/firebase/nanopb.git",
|
||||
"state" : {
|
||||
"revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
|
||||
"version" : "2.30909.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "promises",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/google/promises.git",
|
||||
"state" : {
|
||||
"revision" : "46c1e6b5ac09d8f82c991061c659f67e573d425d",
|
||||
"version" : "2.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639",
|
||||
"version" : "1.19.0"
|
||||
"revision" : "930b78a63f47549c81e6e63c9172584f7d3dfdd6",
|
||||
"version" : "8.52.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
"version" : 3
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -137,7 +137,12 @@ static void loadPresets(void) {
|
|||
if(fileHandle) {
|
||||
NSError *err;
|
||||
NSData *data;
|
||||
data = [fileHandle readDataToEndOfFileAndReturnError:&err];
|
||||
if(@available(macOS 10.15, *)) {
|
||||
data = [fileHandle readDataToEndOfFileAndReturnError:&err];
|
||||
} else {
|
||||
data = [fileHandle readDataToEndOfFile];
|
||||
err = nil;
|
||||
}
|
||||
if(!err && data) {
|
||||
equalizer_presets = json_parse(data.bytes, data.length);
|
||||
|
||||
|
@ -276,15 +281,17 @@ void equalizerLoadPreset(AudioUnit au) {
|
|||
}
|
||||
|
||||
void equalizerApplyPreset(AudioUnit au, const NSDictionary *preset) {
|
||||
if(au && preset) {
|
||||
if(preset) {
|
||||
@synchronized(cog_equalizer_band_settings) {
|
||||
if(!cog_equalizer_band_settings)
|
||||
cog_equalizer_band_settings = _cog_equalizer_band_settings();
|
||||
}
|
||||
|
||||
AudioUnitParameterValue paramValue = 0;
|
||||
if(AudioUnitGetParameter(au, kGraphicEQParam_NumberOfBands, kAudioUnitScope_Global, 0, ¶mValue))
|
||||
return;
|
||||
if(au) {
|
||||
AudioUnitParameterValue paramValue = 0;
|
||||
if(AudioUnitGetParameter(au, kGraphicEQParam_NumberOfBands, kAudioUnitScope_Global, 0, ¶mValue))
|
||||
return;
|
||||
}
|
||||
|
||||
float presetValues[31];
|
||||
interpolateBands(presetValues, preset);
|
||||
|
@ -292,10 +299,14 @@ void equalizerApplyPreset(AudioUnit au, const NSDictionary *preset) {
|
|||
float preamp = getPreamp(preset);
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setFloat:preamp forKey:[cog_equalizer_band_settings objectAtIndex:0]];
|
||||
AudioUnitSetParameter(au, kGraphicEQParam_NumberOfBands, kAudioUnitScope_Global, 0, 1, 0);
|
||||
if(au) {
|
||||
AudioUnitSetParameter(au, kGraphicEQParam_NumberOfBands, kAudioUnitScope_Global, 0, 1, 0);
|
||||
}
|
||||
for(unsigned int i = 0; i < 31; ++i) {
|
||||
[[NSUserDefaults standardUserDefaults] setFloat:presetValues[i] forKey:[cog_equalizer_band_settings objectAtIndex:i + 1]];
|
||||
AudioUnitSetParameter(au, i, kAudioUnitScope_Global, 0, presetValues[i], 0);
|
||||
if(au) {
|
||||
AudioUnitSetParameter(au, i, kAudioUnitScope_Global, 0, presetValues[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,19 @@
|
|||
#import "FeedbackSocket.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface FeedbackController : NSWindowController <FeedbackSocketDelegate> {
|
||||
IBOutlet NSTextField* fromView;
|
||||
IBOutlet NSTextField* subjectView;
|
||||
@interface FeedbackController : NSWindowController {
|
||||
IBOutlet NSTextField* nameView;
|
||||
IBOutlet NSTextField* emailView;
|
||||
IBOutlet NSTextView* messageView;
|
||||
IBOutlet NSProgressIndicator* sendingIndicator;
|
||||
|
||||
FeedbackSocket* feedbackSocket;
|
||||
}
|
||||
|
||||
- (IBAction)sendFeedback:(id)sender;
|
||||
- (IBAction)cancel:(id)sender;
|
||||
|
||||
- (BOOL)waitForCompletion;
|
||||
|
||||
- (NSString *)name;
|
||||
- (NSString *)email;
|
||||
- (NSString *)comments;
|
||||
|
||||
@end
|
||||
|
|
|
@ -10,62 +10,67 @@
|
|||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation FeedbackController
|
||||
@implementation FeedbackController {
|
||||
BOOL showing;
|
||||
BOOL sent;
|
||||
|
||||
NSString *name;
|
||||
NSString *email;
|
||||
NSString *comments;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return [super initWithWindowNibName:@"Feedback"];
|
||||
self = [super initWithWindowNibName:@"Feedback"];
|
||||
if(self) {
|
||||
showing = NO;
|
||||
sent = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)showWindow:(id)sender {
|
||||
[fromView setStringValue:@""];
|
||||
[subjectView setStringValue:@""];
|
||||
[nameView setStringValue:@""];
|
||||
[emailView setStringValue:@""];
|
||||
[messageView setString:@""];
|
||||
|
||||
[super showWindow:sender];
|
||||
}
|
||||
|
||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
||||
if([(NSNumber *)CFBridgingRelease(contextInfo) boolValue] == YES) {
|
||||
[[self window] close];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)feedbackDidNotSend:(FeedbackSocket *)feedback {
|
||||
ALog(@"Error sending feedback");
|
||||
|
||||
[sendingIndicator stopAnimation:self];
|
||||
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:NSLocalizedString(@"FeedbackFailedMessageText", @"")];
|
||||
[alert setInformativeText:NSLocalizedString(@"FeedbackFailedInformativeText", @"")];
|
||||
|
||||
[alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain([NSNumber numberWithBool:NO])];
|
||||
}
|
||||
|
||||
- (void)feedbackDidSend:(FeedbackSocket *)feedback {
|
||||
[sendingIndicator stopAnimation:self];
|
||||
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:NSLocalizedString(@"FeedbackSuccessMessageText", @"")];
|
||||
[alert setInformativeText:NSLocalizedString(@"FeedbackSuccessInformativeText", @"")];
|
||||
|
||||
[alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain([NSNumber numberWithBool:YES])];
|
||||
|
||||
showing = YES;
|
||||
}
|
||||
|
||||
- (IBAction)sendFeedback:(id)sender {
|
||||
[sendingIndicator startAnimation:self];
|
||||
name = [nameView stringValue];
|
||||
email = [emailView stringValue];
|
||||
comments = [messageView string];
|
||||
|
||||
// Using this so that if its a bad connection, it doesnt sit there looking stupid..or should it
|
||||
feedbackSocket = [[FeedbackSocket alloc] init];
|
||||
[feedbackSocket setDelegate:self];
|
||||
|
||||
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
|
||||
[feedbackSocket sendFeedback:[fromView stringValue] subject:[subjectView stringValue] message:[messageView string] version:version];
|
||||
[[self window] close];
|
||||
sent = YES;
|
||||
showing = NO;
|
||||
}
|
||||
|
||||
- (IBAction)cancel:(id)sender {
|
||||
[[self window] close];
|
||||
sent = NO;
|
||||
showing = NO;
|
||||
}
|
||||
|
||||
- (BOOL)waitForCompletion {
|
||||
while(showing) {
|
||||
usleep(2000);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
- (NSString *)name {
|
||||
return name;
|
||||
}
|
||||
|
||||
- (NSString *)email {
|
||||
return email;
|
||||
}
|
||||
|
||||
- (NSString *)comments {
|
||||
return comments;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -23,8 +23,28 @@
|
|||
NSString *title = nil;
|
||||
NSString *artist = nil;
|
||||
if(metadata) {
|
||||
title = [metadata valueForKey:@"title"];
|
||||
artist = [metadata valueForKey:@"artist"];
|
||||
id _title = [metadata valueForKey:@"title"];
|
||||
id _artist = [metadata valueForKey:@"artist"];
|
||||
|
||||
if([_title isKindOfClass:[NSArray class]]) {
|
||||
NSArray *titlearray = _title;
|
||||
title = [titlearray componentsJoinedByString:@", "];
|
||||
} else if([_title isKindOfClass:[NSString class]]) {
|
||||
title = _title;
|
||||
} else if([_title isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *titlenumber = _title;
|
||||
title = [NSString stringWithFormat:@"%@", titlenumber];
|
||||
}
|
||||
|
||||
if([_artist isKindOfClass:[NSArray class]]) {
|
||||
NSArray *artistarray = _artist;
|
||||
artist = [artistarray componentsJoinedByString:@", "];
|
||||
} else if([_artist isKindOfClass:[NSString class]]) {
|
||||
artist = _artist;
|
||||
} else if([_artist isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *artistnumber = _artist;
|
||||
artist = [NSString stringWithFormat:@"%@", artistnumber];
|
||||
}
|
||||
}
|
||||
|
||||
if(title && [title length]) {
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
if(path) {
|
||||
[panel setDirectoryURL:[NSURL fileURLWithPath:path]];
|
||||
}
|
||||
[panel setTitle:@"Open to choose tree path"];
|
||||
NSInteger result = [panel runModal];
|
||||
if(result == NSModalResponseOK) {
|
||||
[[SandboxBroker sharedSandboxBroker] addFolderIfMissing:[panel URL]];
|
||||
|
|
|
@ -24,3 +24,8 @@
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface SecondsFractionFormatter : NSFormatter {
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -99,3 +99,83 @@
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SecondsFractionFormatter
|
||||
|
||||
- (NSString *)stringForObjectValue:(id)object {
|
||||
NSString *result = nil;
|
||||
double value;
|
||||
unsigned days = 0;
|
||||
unsigned hours = 0;
|
||||
unsigned minutes = 0;
|
||||
float seconds = 0.0;
|
||||
|
||||
if(nil == object || NO == [object isKindOfClass:[NSNumber class]] || isnan([object doubleValue])) {
|
||||
return @"";
|
||||
}
|
||||
|
||||
value = [object doubleValue];
|
||||
|
||||
seconds = fmod(value, 60.0);
|
||||
minutes = (unsigned)floor(value / 60.0);
|
||||
|
||||
while(60 <= minutes) {
|
||||
minutes -= 60;
|
||||
++hours;
|
||||
}
|
||||
|
||||
while(24 <= hours) {
|
||||
hours -= 24;
|
||||
++days;
|
||||
}
|
||||
|
||||
if(0 < days) {
|
||||
result = [NSString stringWithFormat:@"%u:%.2u:%.2u:%06.3f", days, hours, minutes, seconds];
|
||||
} else if(0 < hours) {
|
||||
result = [NSString stringWithFormat:@"%u:%.2u:%06.3f", hours, minutes, seconds];
|
||||
} else if(0 < minutes) {
|
||||
result = [NSString stringWithFormat:@"%u:%06.3f", minutes, seconds];
|
||||
} else {
|
||||
result = [NSString stringWithFormat:@"0:%06.3f", seconds];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error {
|
||||
NSScanner *scanner = nil;
|
||||
BOOL result = NO;
|
||||
double value = 0.0;
|
||||
double seconds = 0.0;
|
||||
|
||||
scanner = [NSScanner scannerWithString:string];
|
||||
|
||||
while(NO == [scanner isAtEnd]) {
|
||||
// Grab a value
|
||||
if([scanner scanDouble:&value]) {
|
||||
seconds *= 60.0;
|
||||
seconds += value;
|
||||
result = YES;
|
||||
}
|
||||
|
||||
// Grab the separator, if present
|
||||
[scanner scanString:@":" intoString:NULL];
|
||||
}
|
||||
|
||||
if(result && NULL != object) {
|
||||
*object = @(seconds);
|
||||
} else if(NULL != error) {
|
||||
*error = @"Couldn't convert value to seconds";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes {
|
||||
NSAttributedString *result = nil;
|
||||
|
||||
result = [[NSAttributedString alloc] initWithString:[self stringForObjectValue:object] attributes:attributes];
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2021-2023 Christopher Snowhill. All rights reserved.</string>
|
||||
<string>Copyright © 2021-2025 Christopher Snowhill. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 48;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -557,16 +557,12 @@
|
|||
83D3C4CB201C654F005564CB /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
ORGANIZATIONNAME = "Christopher Snowhill";
|
||||
TargetAttributes = {
|
||||
83D3C4D3201C654F005564CB = {
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1620;
|
||||
ORGANIZATIONNAME = "";
|
||||
};
|
||||
buildConfigurationList = 83D3C4CE201C654F005564CB /* Build configuration list for PBXProject "libAdPlug" */;
|
||||
compatibilityVersion = "Xcode 8.0";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
|
@ -725,6 +721,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -740,7 +737,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
|
@ -790,6 +787,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -799,7 +797,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
|
@ -812,6 +810,7 @@
|
|||
83D3C4DD201C6550005564CB /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -828,7 +827,11 @@
|
|||
);
|
||||
INFOPLIST_FILE = AdPlug/Info.plist;
|
||||
INSTALL_PATH = "@loader_path/../Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libAdPlug;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -838,6 +841,7 @@
|
|||
83D3C4DE201C6550005564CB /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -850,7 +854,11 @@
|
|||
);
|
||||
INFOPLIST_FILE = AdPlug/Info.plist;
|
||||
INSTALL_PATH = "@loader_path/../Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libAdPlug;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
|
|
@ -486,12 +486,6 @@
|
|||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0900;
|
||||
TargetAttributes = {
|
||||
8DC2EF4F0486A6940098B216 = {
|
||||
DevelopmentTeam = "";
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Dumb" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -631,17 +631,12 @@
|
|||
8359FF3317FEF39F0060F3ED /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
ORGANIZATIONNAME = "Christopher Snowhill";
|
||||
TargetAttributes = {
|
||||
8359FF3B17FEF39F0060F3ED = {
|
||||
DevelopmentTeam = "";
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1640;
|
||||
ORGANIZATIONNAME = "";
|
||||
};
|
||||
buildConfigurationList = 8359FF3617FEF39F0060F3ED /* Build configuration list for PBXProject "File_Extractor" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
|
@ -793,6 +788,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
|
@ -810,7 +806,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
|
@ -852,6 +848,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -862,7 +859,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
SDKROOT = macosx;
|
||||
|
@ -872,6 +869,7 @@
|
|||
8359FF6517FEF39F0060F3ED /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -896,6 +894,7 @@
|
|||
8359FF6617FEF39F0060F3ED /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2021-2023 Christopher Snowhill. All rights reserved.</string>
|
||||
<string>Copyright © 2021-2025 Christopher Snowhill. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -627,16 +627,11 @@
|
|||
0867D690FE84028FC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
TargetAttributes = {
|
||||
8DC2EF4F0486A6940098B216 = {
|
||||
DevelopmentTeam = N6E749HJ2X;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1620;
|
||||
};
|
||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "GME" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
|
@ -745,6 +740,7 @@
|
|||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
|
@ -780,6 +776,7 @@
|
|||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -836,6 +833,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -845,7 +843,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
OBJROOT = ../../build;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
|
@ -884,6 +882,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
|
@ -892,7 +891,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
OBJROOT = ../../build;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "Bml_Parser.h"
|
||||
|
||||
/* Copyright (C) 2013-2023 Christopher Snowhill. This module is free
|
||||
/* Copyright (C) 2013-2025 Christopher Snowhill. This module is free
|
||||
software; you can redistribute it and/or modify it under the terms of
|
||||
the GNU Lesser General Public License as published by the Free Software
|
||||
Foundation; either version 2.1 of the License, or (at your option) any
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Copyright (C) 2013-2023 Christopher Snowhill. This module is free
|
||||
/* Copyright (C) 2013-2025 Christopher Snowhill. This module is free
|
||||
software; you can redistribute it and/or modify it under the terms of
|
||||
the GNU Lesser General Public License as published by the Free Software
|
||||
Foundation; either version 2.1 of the License, or (at your option) any
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -248,17 +248,12 @@
|
|||
8343792C17F97BDB00584396 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
ORGANIZATIONNAME = "Christopher Snowhill";
|
||||
TargetAttributes = {
|
||||
8343793417F97BDB00584396 = {
|
||||
DevelopmentTeam = N6E749HJ2X;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1620;
|
||||
ORGANIZATIONNAME = "";
|
||||
};
|
||||
buildConfigurationList = 8343792F17F97BDB00584396 /* Build configuration list for PBXProject "HighlyAdvanced" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
|
@ -358,6 +353,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
|
@ -374,7 +370,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
|
@ -416,6 +412,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -425,7 +422,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
SDKROOT = macosx;
|
||||
|
@ -435,6 +432,7 @@
|
|||
8343795E17F97BDB00584396 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -458,6 +456,7 @@
|
|||
8343795F17F97BDB00584396 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2021-2023 Christopher Snowhill. All rights reserved.</string>
|
||||
<string>Copyright © 2021-2025 Christopher Snowhill. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -187,17 +187,12 @@
|
|||
8360EF0717F92C91005208A4 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
ORGANIZATIONNAME = "Christopher Snowhill";
|
||||
TargetAttributes = {
|
||||
8360EF0F17F92C91005208A4 = {
|
||||
DevelopmentTeam = N6E749HJ2X;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1620;
|
||||
ORGANIZATIONNAME = "";
|
||||
};
|
||||
buildConfigurationList = 8360EF0A17F92C91005208A4 /* Build configuration list for PBXProject "HighlyExperimental" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 12.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
|
@ -295,6 +290,7 @@
|
|||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
|
@ -312,7 +308,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
|
@ -353,6 +349,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -363,7 +360,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
OTHER_CFLAGS = "-Wframe-larger-than=4000";
|
||||
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
|
||||
};
|
||||
|
@ -372,6 +369,7 @@
|
|||
8360EF3917F92C91005208A4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
@ -397,6 +395,7 @@
|
|||
8360EF3A17F92C91005208A4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
LastUpgradeVersion = "1640"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue