Compare commits
3192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1226aec2fc | |||
| 99a55c4452 | |||
| 14cf721094 | |||
| 515bc4ba53 | |||
| 25dfe4b57a | |||
| 9a4a3d3977 | |||
| 4fd0cd3151 | |||
| 6761218e51 | |||
| 31b9c50721 | |||
| f589244fb6 | |||
| 7a79f09a31 | |||
| db65113c5f | |||
| f3f94e478b | |||
| c39eeb9638 | |||
| 493c8bbc83 | |||
| 052fdd67cd | |||
| ed8f44d102 | |||
| 1d1acc0c34 | |||
| 1860b2ec8a | |||
| 7ad4f753a4 | |||
| d5a78f0843 | |||
| 32494e72a5 | |||
| 0396c0ecd2 | |||
| 51a447852f | |||
| 1684b2cc5f | |||
| 1dee902a5f | |||
| 79314c4461 | |||
| 2d6309d6fc | |||
| c69338bc0d | |||
| 39c3314d00 | |||
| db6905bf57 | |||
| 800326d37a | |||
| c708f871ec | |||
| 77a53594ba | |||
| 36a6b9d42a | |||
| b6d0ff02c2 | |||
| f21e2df4cf | |||
| 84a90b7cc3 | |||
| ebbbeb0d66 | |||
| cb8545346d | |||
| 5c6b8eda4f | |||
| ea95a8adef | |||
| 6eb980d1b2 | |||
| 48b7745a49 | |||
| 9ef4dd4247 | |||
| 952ff84b99 | |||
| b77e6096b8 | |||
| 7f4441ffbe | |||
| 8bacb5dfb8 | |||
| a6734c7a91 | |||
| db452a19da | |||
| 99737469e2 | |||
| b13396cce2 | |||
| d1f8b78dd4 | |||
| 4bdab0051f | |||
| 634b13a630 | |||
| 275658b6db | |||
| 9054f41434 | |||
| fa7993fe3e | |||
| 46cfedb117 | |||
| 212cb8e298 | |||
| 7394dd512d | |||
| 96aacac10c | |||
| 4c32a0aba7 | |||
| 0f243b927c | |||
| fce86815f4 | |||
| 3b4f28d173 | |||
| b854f4037f | |||
| 30bd84a935 | |||
| 0a8398dd99 | |||
| 01cc0cd787 | |||
| 0d4b68239a | |||
| 68a8c42176 | |||
| 8b733728af | |||
| 1fa464ec3e | |||
| 3368bd75c7 | |||
| 827c66f4f6 | |||
| 21dc2c761d | |||
| 529f387695 | |||
| dacd08dc5d | |||
| 072ca731e2 | |||
| a3010f50be | |||
| 4be6584850 | |||
| 24b646dec0 | |||
| 7d76cbddd6 | |||
| 90ca9b9ff4 | |||
| 2b9c213f89 | |||
| adfbe0d6cd | |||
| 125ac166d1 | |||
| 1e33c4a922 | |||
| a8250db2bb | |||
| 04921dd727 | |||
| 06da60c555 | |||
| 32edc7f33f | |||
| 4f92bd5938 | |||
| 4aa029183b | |||
| 3f165040e2 | |||
| 754a32fa16 | |||
| 57b860d8ca | |||
| 0c67429a38 | |||
| aa58a5d208 | |||
| 3894c580bf | |||
| f1bf932a84 | |||
| 8ab448b119 | |||
| 39a5d0cb94 | |||
| 7d68eeac88 | |||
| 92fd59fe6a | |||
| f79683d459 | |||
| 83dba26ea6 | |||
| 809f709ba6 | |||
| 6fe1ffa3bb | |||
| d117a5209f | |||
| ed1f1d2bf3 | |||
| fd3f52ea36 | |||
| 4c61103e34 | |||
| dc3fa3e51f | |||
| e2d7c5dfb3 | |||
| 7e2cac5292 | |||
| a80ab01227 | |||
| 73e08e51ba | |||
| fd1458ce1b | |||
| 9c643f0356 | |||
| 35b78d55a4 | |||
| bd9c6989c2 | |||
| 72e4b60374 | |||
| 48b265e136 | |||
| 7865ebb6d5 | |||
| f765099503 | |||
| d5dd4937eb | |||
| 1bba276aaf | |||
| 5233c4aa21 | |||
| 0b7f35b289 | |||
| 46c59f382f | |||
| 8854720e8a | |||
| 42d3235a66 | |||
| 5cba81cf35 | |||
| a5edf487dd | |||
| 728dd5a4e0 | |||
| ac4061b9d2 | |||
| ec84a107a4 | |||
| 803d331711 | |||
| 422cf429e6 | |||
| 93de5350b9 | |||
| 9bf3ee722c | |||
| 1782208fde | |||
| 84a94a6109 | |||
| c2a4a35c2e | |||
| f3d4220cab | |||
| 8219bb374d | |||
| 80df89d0db | |||
| ecdc58c1c9 | |||
| da63b4a596 | |||
| 4e315353c8 | |||
| 720ec2beea | |||
| a235259b5e | |||
| 97c165b951 | |||
| da6f32a1c0 | |||
| d3a81f4f28 | |||
| dd51ba396b | |||
| 7501c8b483 | |||
| 5e7513d0a0 | |||
| cd2fd126c4 | |||
| 0cd375f03c | |||
| 588a6bcb4f | |||
| 72e39ba1c9 | |||
| 6534aa0c27 | |||
| e42489b3e9 | |||
| 2b9064cb6e | |||
| 197e88cdcc | |||
| 4ac4889912 | |||
| 4ee04cd444 | |||
| 43dccc8807 | |||
| df9faba689 | |||
| 5272caf7f4 | |||
| 1e04c4c689 | |||
| dd055fc858 | |||
| 5c0673049c | |||
| f240952bcc | |||
| e787a241c0 | |||
| 3867cad94d | |||
| be30e6bf8f | |||
| 1fb6428ffa | |||
| b31a54e972 | |||
| 75e2cb97f7 | |||
| ac8dbd7a56 | |||
| d8a659c6f2 | |||
| af90d89b6b | |||
| a19296e3cf | |||
| 85c03925fb | |||
| 22413b82ca | |||
| e8aaa9cb13 | |||
| 2385ab700c | |||
| 242ed45f65 | |||
| e420752e82 | |||
| 54109043b8 | |||
| ddd4751f87 | |||
| 492c75cb7b | |||
| 1e0728af64 | |||
| 1cc6983b96 | |||
| 8311b57ca6 | |||
| 510efae066 | |||
| b3ba90f5fa | |||
| e10595df31 | |||
| b51d30f84d | |||
| ea8b52ee2c | |||
| 9575eebac4 | |||
| ace8e64850 | |||
| 4ee26f4ca3 | |||
| a55c93e1f5 | |||
| 400ab8766c | |||
| 90b8d639f1 | |||
| 525b6fe168 | |||
| f96a8fe369 | |||
| 150586d7fe | |||
| 09194fd488 | |||
| e619b64175 | |||
| 795ed65797 | |||
| 5b82dd8733 | |||
| 9c5307a48a | |||
| b1fe405861 | |||
| 869637af2e | |||
| 414b56edec | |||
| 334bc4f551 | |||
| e288c3b44a | |||
| 91900b0599 | |||
| 29ae74d4bb | |||
| a2c24ff946 | |||
| 61de399806 | |||
| f48a1e3a31 | |||
| 4a8c3b50b6 | |||
| 852ad41176 | |||
| 880d555060 | |||
| 5523913a81 | |||
| a7da29804d | |||
| cae4b1a062 | |||
| 2b16ff3ca8 | |||
| 5aecce5a0d | |||
| d85ddf8aae | |||
| 788f5398b0 | |||
| 964723691a | |||
| 2475d4652c | |||
| 79eb2ea26e | |||
| f3672ab26b | |||
| f757fbce4d | |||
| e5e3b05015 | |||
| eccf679ca9 | |||
| 3fc9d3a1be | |||
| 3f071a90a4 | |||
| 413d55aaa5 | |||
| 6d8062a2a7 | |||
| 6990478369 | |||
| e37634e908 | |||
| 63c9d95b84 | |||
| 63c35248be | |||
| 6685e0a078 | |||
| 7789cc8e32 | |||
| 1d6622aa2b | |||
| eeff730034 | |||
| 23fd7451d0 | |||
| bc3a96308e | |||
| 8e976a92f7 | |||
| 7a23330ffe | |||
| 6ea3b5445f | |||
| 091e676db3 | |||
| 6ad171781a | |||
| d157d66678 | |||
| 4da6364fc2 | |||
| 7b95753cf7 | |||
| 8f3d496541 | |||
| 0a6c17c544 | |||
| f11d7eeadf | |||
| 22a7bcd5ad | |||
| ecc51c8c55 | |||
| 55a73b8df9 | |||
| c40b4ce3f2 | |||
| 47260fc70b | |||
| c38854fe3d | |||
| df6e4617e4 | |||
| 56423b9ef1 | |||
| 6fd23f0984 | |||
| 325fca5215 | |||
| 3fde27d890 | |||
| 445f63f350 | |||
| 603e3e23ac | |||
| 2011421e18 | |||
| 7c0e68a33e | |||
| f65e83c349 | |||
| 942abab8f9 | |||
| 8eb7a94e1c | |||
| 6b35a4534f | |||
| dac9379449 | |||
| a6ddeaf5f1 | |||
| 30821e978b | |||
| c0e7a5b8b0 | |||
| 8b24255557 | |||
| 46e7e96eb3 | |||
| 41cc82e84b | |||
| 2866c6cf28 | |||
| 0c7fa1b955 | |||
| aa7df8183f | |||
| 58760e6184 | |||
| 52ca70d98e | |||
| d4fc87fc9c | |||
| 2187316bce | |||
| 52dee31270 | |||
| 22d3e3b563 | |||
| 4e613c5d4b | |||
| 07c8857558 | |||
| 9bd1678299 | |||
| 19281868dc | |||
| 5133fe5115 | |||
| 2eb29d538b | |||
| 0af075b5ff | |||
| ef2b813776 | |||
| a6f93a501a | |||
| 767807cd22 | |||
| 86319127e3 | |||
| 179e1c4dbc | |||
| 945d74cbc9 | |||
| b8636a3dbf | |||
| 4e6c758daf | |||
| 4bd02eada6 | |||
| bd32a682f7 | |||
| 03274a5b95 | |||
| 6f62fed65c | |||
| cba37819d1 | |||
| 4b85104c4f | |||
| 0d91d63cf3 | |||
| abc6e37e6e | |||
| 812cb62617 | |||
| a211c2f5e4 | |||
| d25398d2c7 | |||
| 654a45833a | |||
| 455763aa9a | |||
| c7456affd5 | |||
| a1ef5e38b6 | |||
| 14bdc6c2ef | |||
| bf886444d2 | |||
| 9fc2dbe063 | |||
| 51c6d13c0f | |||
| e7ac63be4d | |||
| d7d043a5ef | |||
| 1002bc36e0 | |||
| 6ff50558a0 | |||
| fa9ab9af7c | |||
| b5aa0c0759 | |||
| 79604c3c0e | |||
| 7a974b2460 | |||
| 7f3d69fdbc | |||
| 7dafac0c34 | |||
| c24aea712b | |||
| 063ad490a2 | |||
| 59768674f1 | |||
| 2cb49e51a0 | |||
| 6b76eddbe3 | |||
| 59934b47de | |||
| 4d417f3dd4 | |||
| dce497fbca | |||
| e63e0ee596 | |||
| 494e9fd61d | |||
| b11e31fbfd | |||
| 1e93122470 | |||
| 5b11129eaa | |||
| ee3d53a673 | |||
| c346baff5b | |||
| 20bee69955 | |||
| 4cd04f192d | |||
| f5f996c14a | |||
| da1ee3baba | |||
| 1d7a5dbede | |||
| 9b878c64f9 | |||
| 1f61472e77 | |||
| b99e64a0e3 | |||
| e49607078e | |||
| 4ef60d7214 | |||
| 40bda4d93f | |||
| 8f4c078fb5 | |||
| 80cc32d77b | |||
| 28fa82378b | |||
| 6a4a88f8f5 | |||
| a114d6cbb5 | |||
| 3acd2a55ad | |||
| 3a90fd5751 | |||
| ec14c98f2d | |||
| 975c682ec3 | |||
| e240351d81 | |||
| 4ec31291c0 | |||
| 7a538141c9 | |||
| bcdc399029 | |||
| 8652ae046b | |||
| 833eface38 | |||
| 7256bc92fa | |||
| 0394d5a44f | |||
| e7217760f3 | |||
| a042dbf305 | |||
| 328fd7329d | |||
| a24a22c39a | |||
| c2710ffe87 | |||
| 782eb650c7 | |||
| 38a3ba74d4 | |||
| d1da9f1f67 | |||
| dcc5a10c7b | |||
| 0051ebace0 | |||
| 0cd61fd197 | |||
| f16ece6048 | |||
| e12059a311 | |||
| cb9fb31214 | |||
| e8c268ef09 | |||
| 6a839600b9 | |||
| 4d1795e90b | |||
| 277127829a | |||
| 58fd9d70d2 | |||
| d2cf61c99a | |||
| 6562a939ea | |||
| 031cf24880 | |||
| 38261a0f2a | |||
| c4a76af397 | |||
| f76ab02bd4 | |||
| 4d392a8b87 | |||
| c969dad6c2 | |||
| 9718502e5d | |||
| 9613b4bfe2 | |||
| 5d2196c119 | |||
| a885fd6d0e | |||
| 3ba4746ff8 | |||
| e1e1c73ae6 | |||
| fbafbe6272 | |||
| 6ae7fc5df1 | |||
| 99f0377ae3 | |||
| 932939c5a8 | |||
| 0c9c846522 | |||
| f5236ab186 | |||
| 2a7f5aa569 | |||
| 1d86aea61c | |||
| 2ded5547ba | |||
| 395278fcdb | |||
| 786a05876e | |||
| caa3b4f7f8 | |||
| 2266a9e01e | |||
| def2547e40 | |||
| 2ac0adaebb | |||
| 4ebb97bf73 | |||
| b05592b0ab | |||
| 9ee58fd454 | |||
| 4f9a0460c4 | |||
| ad61392318 | |||
| a852ecf8ce | |||
| d6d6ec03f1 | |||
| fce72610bb | |||
| 4a368ab905 | |||
| f1f7fba3b9 | |||
| ca07e210a7 | |||
| 5f3ae527c9 | |||
| db13af1935 | |||
| efd11f32a2 | |||
| 6489fb586b | |||
| 427212705f | |||
| f6276164f3 | |||
| dcb934fc2e | |||
| c2c3846930 | |||
| 03d4e4acc4 | |||
| 4e647bda07 | |||
| ab2295b68f | |||
| 940c6146e8 | |||
| b56673bbce | |||
| 2102625021 | |||
| 884fe40fd1 | |||
| 3baf9d0e81 | |||
| 248832b266 | |||
| bf8c889631 | |||
| 273dca2fa0 | |||
| e80b016cbe | |||
| 8c19497340 | |||
| 5deb1df8db | |||
| 14a0df03e8 | |||
| bc44efb91d | |||
| 799dfb732b | |||
| 813b06a3a4 | |||
| 47c9b0a17e | |||
| 44795b8614 | |||
| 5acb2e5165 | |||
| 682205a3d4 | |||
| 7441311e3d | |||
| 6f48dac4cc | |||
| bc078e5158 | |||
| 63416bf28f | |||
| 0152bc0d10 | |||
| 7e491ef2b2 | |||
| 6f532fa0e5 | |||
| 25b3ed3532 | |||
| 7a77a20a67 | |||
| 2957d85924 | |||
| 2b968766b9 | |||
| 78cb70a5f9 | |||
| 16e4e0fa7c | |||
| c031b3cc6a | |||
| bbc4ebecab | |||
| 71ad3b1715 | |||
| af8bbefcab | |||
| 26043269e4 | |||
| c9edf7cf78 | |||
| 77910afe57 | |||
| 4db1328bc5 | |||
| e634564b26 | |||
| dae5faedb0 | |||
| 1f236d8f20 | |||
| 370817ba60 | |||
| 2873abcbc0 | |||
| e89c183231 | |||
| ca9e6a4f94 | |||
| b11131ce0c | |||
| 2d2ca1d791 | |||
| 85a8f965e5 | |||
| 88545ccae5 | |||
| 3832b25f30 | |||
| 15ad5245bf | |||
| 8aa9e35f8c | |||
| 81b13f78ea | |||
| 9b9ec2c21a | |||
| f3c9bc114c | |||
| ff231b42ff | |||
| 9c33f4aea8 | |||
| f55583330b | |||
| 2c8e4467c3 | |||
| 15f9d15ba0 | |||
| 279799ad70 | |||
| ea6b384980 | |||
| ef63ec3fe9 | |||
| de8c5b1824 | |||
| 9e2eb5dc23 | |||
| 5d1207104a | |||
| d547f52d8f | |||
| 56e9cb69d3 | |||
| 70973035de | |||
| c8a30592e8 | |||
| d8258984b4 | |||
| c423b6c8b7 | |||
| 4cc1c64646 | |||
| 0e1fd34c89 | |||
| 10f2531b6c | |||
| ecd327e5e1 | |||
| 510bccb6ac | |||
| 32330c5eef | |||
| e00ef16893 | |||
| fd8143fea0 | |||
| 08a4840298 | |||
| a16f933e3a | |||
| 9d61f429a5 | |||
| b8de63d608 | |||
| dcc0f6e25d | |||
| 21684fac69 | |||
| 329333ca8b | |||
| 91e20dc12e | |||
| aecfa0b426 | |||
| 9675068706 | |||
| cbd55ad31e | |||
| 5eb1753d31 | |||
| e277f5e268 | |||
| 145bf52b7a | |||
| fe7bdc93eb | |||
| d889435334 | |||
| dad17a7d91 | |||
| 830adf6ae8 | |||
| babfa70cab | |||
| 258eadd003 | |||
| 6dd1643a70 | |||
| 48916ebd76 | |||
| 506d316f55 | |||
| 490b71378e | |||
| 0e8b0c520e | |||
| 0136df1e62 | |||
| 98731bb574 | |||
| 27dfb7fd11 | |||
| 6457df54bb | |||
| 07f3ed0419 | |||
| dabb99de8d | |||
| 254ebe5f96 | |||
| b32a366137 | |||
| 3633b65e04 | |||
| ce74098b40 | |||
| 5769efe48e | |||
| 02051ab51a | |||
| 02c0620141 | |||
| d7895db166 | |||
| 0f84e32520 | |||
| 1a911be617 | |||
| a0565dc9bb | |||
| 52569ef562 | |||
| 661bd054aa | |||
| eee4aca40b | |||
| 04cf832fe6 | |||
| 2346426b6e | |||
| 8253014fe9 | |||
| 57e516720e | |||
| d229afb83d | |||
| 3cd1f30577 | |||
| 94347d4e64 | |||
| 6fe6c370c2 | |||
| 42acafcbc3 | |||
| ebd42ab619 | |||
| b10780f119 | |||
| d657c51be8 | |||
| 30007263e5 | |||
| ff3d31e17f | |||
| d281b25723 | |||
| 2a3b5ab9e9 | |||
| d9f57c099b | |||
| 32f72c7471 | |||
| 5993ef0baa | |||
| d96be76875 | |||
| 0e2e9e9a29 | |||
| 7c88e4a16b | |||
| eac0539ea0 | |||
| b82d2cf055 | |||
| 7d4028b2df | |||
| 0349bcc500 | |||
| 2f5b86dd38 | |||
| 5ec11a017c | |||
| b3f7df6f5b | |||
| d57eca57a9 | |||
| 71ce90d083 | |||
| 296abf03de | |||
| 1ab8902621 | |||
| fd70b0bca6 | |||
| 101d284a18 | |||
| 2f66139053 | |||
| 8e64adb2d3 | |||
| c5a917b401 | |||
| 27ab921e5b | |||
| 369f52a0d7 | |||
| 9007b77355 | |||
| fc9cb2fbd8 | |||
| 46b9efc5a2 | |||
| 90cedbea49 | |||
| 0b243f1aaf | |||
| bc386af5c5 | |||
| 4ca8b4080a | |||
| c7273ada8e | |||
| 724d6267c8 | |||
| 653edd4b02 | |||
| 0b0ae75ccf | |||
| 226efffd21 | |||
| 88d76bf069 | |||
| 67f183f206 | |||
| 0408f9b7df | |||
| e3d99412a2 | |||
| cd15b1775e | |||
| 2916efee21 | |||
| 6c367cad6e | |||
| fde02abc35 | |||
| fdce192aab | |||
| a4d357de06 | |||
| 773d4664ff | |||
| 72e3398baf | |||
| f1bbf2e48a | |||
| bfcde9ae8a | |||
| b1ff2a05af | |||
| 561e95528f | |||
| 9ea32713b5 | |||
| 3d76e1c514 | |||
| 61097d467c | |||
| 46f18ba114 | |||
| ef7ce82828 | |||
| 921a820867 | |||
| e7c4681e9a | |||
| 877ecda14d | |||
| 079e4b1edb | |||
| 65bcbe0dc8 | |||
| bbabc9705e | |||
| 520938da57 | |||
| 97d09b4cee | |||
| 0a147693e8 | |||
| 55643d6370 | |||
| 051828344e | |||
| 3e56e7f562 | |||
| e8e1a05f38 | |||
| 5f50d085ad | |||
| f75020da5a | |||
| 75bf45376c | |||
| d2e485cc6d | |||
| fdd5070c3b | |||
| 5048956ff0 | |||
| 080c076643 | |||
| 3f3a3ac4f1 | |||
| 9fd3e5f529 | |||
| e0408d8edf | |||
| 5008024e73 | |||
| f9c09cc845 | |||
| 520dd5c779 | |||
| 4dd53ab942 | |||
| 0604b00360 | |||
| a931314d3f | |||
| 2987c87645 | |||
| 649bf575e2 | |||
| ad3c4b81e2 | |||
| 530002e168 | |||
| 6eadccc99d | |||
| 8a735d0ba9 | |||
| 88c0d22e7c | |||
| e5431bbef7 | |||
| de51ba088e | |||
| bf9c36408a | |||
| 12db67f548 | |||
| e25ba747e6 | |||
| 11b44d29e6 | |||
| 75841642bf | |||
| 1ab082caaf | |||
| 82a990e08e | |||
| 5ab4e584ee | |||
| 4ec67e6226 | |||
| 21b71b2fd2 | |||
| f91445029d | |||
| fea74124c8 | |||
| 0135ddc2e5 | |||
| 5f7268c0cc | |||
| c484935542 | |||
| ff696bc1fe | |||
| 8fa3f6da64 | |||
| 249afde5f9 | |||
| a535169bbd | |||
| 22d77b87a3 | |||
| 67ab9ec466 | |||
| 0da55fd912 | |||
| 5048322fff | |||
| f313f9c117 | |||
| d6326c1791 | |||
| d050af7eeb | |||
| 2be4c93f9f | |||
| 09826cc684 | |||
| e4e240fa9b | |||
| 6be752a9b1 | |||
| faefa1d554 | |||
| 56a33ef955 | |||
| dadf7a4cf2 | |||
| 3af2ff7b25 | |||
| 72ce0d1e9c | |||
| 3cd4cfe73d | |||
| 74139c914d | |||
| cb83f48105 | |||
| a14f651034 | |||
| 958fa34272 | |||
| af626fdded | |||
| 7daefd4e77 | |||
| 32bda7b94c | |||
| 77e317609b | |||
| ce9222b686 | |||
| 8a0d1e5191 | |||
| d874b6bb27 | |||
| 00694826ef | |||
| 058d57ef03 | |||
| e8ce8924d1 | |||
| ec13cf6c9c | |||
| a69d608a1e | |||
| aefa11b6ad | |||
| cee2d226d2 | |||
| f08a7b341f | |||
| 15e6821147 | |||
| 3a16fed279 | |||
| ef7eced7a7 | |||
| a729007887 | |||
| 323162c49f | |||
| ea4379c5b7 | |||
| 6124f447cd | |||
| 47f1db36a7 | |||
| b88b57868a | |||
| 9fd7349220 | |||
| cfec20830b | |||
| ebee8da05c | |||
| 7cadbebe9f | |||
| e9a7218a06 | |||
| 8f2bbc434c | |||
| b13fead555 | |||
| 2f2d941edb | |||
| 740e7d979c | |||
| 464a7c0545 | |||
| 55c329b462 | |||
| 5d5eff7502 | |||
| 4b7cbb50ab | |||
| be552a35e6 | |||
| 1deb8b6160 | |||
| c465718ddd | |||
| d1bb04d588 | |||
| d70a98fe03 | |||
| 9d01ac440d | |||
| 44a940d8d6 | |||
| e07166e93c | |||
| d1077795de | |||
| 31b02e044f | |||
| 552d3a85af | |||
| 6fcbb7c738 | |||
| e09468cdca | |||
| 36edd80073 | |||
| 0a361fa9e2 | |||
| 2b013fc54e | |||
| e3a825b37c | |||
| 9fc01934c2 | |||
| 879b79740f | |||
| e18f7ca3e4 | |||
| d4f3490a88 | |||
| 1e8feeff03 | |||
| a35b9bbd3e | |||
| ba0644254c | |||
| 39e4756b37 | |||
| b4aea294c3 | |||
| 10ff6cba9c | |||
| 61e43d4039 | |||
| 9222d89de2 | |||
| e1dcfa4b9b | |||
| 1c04cbad04 | |||
| f54946f9cb | |||
| e893211b65 | |||
| 6d4adc6ad6 | |||
| 07bb5f1322 | |||
| a79e8f3d95 | |||
| e9505b164e | |||
| c51df897db | |||
| c4ef7399f8 | |||
| 653f0a1405 | |||
| 2b14c1d623 | |||
| 59d016dfcd | |||
| db90dbe9f2 | |||
| 9d4f601a8a | |||
| c68a537ae8 | |||
| 2266fb76b9 | |||
| dd6c5270b6 | |||
| e6be113903 | |||
| 49e5c53591 | |||
| 95ea3fdd05 | |||
| 5691dceb38 | |||
| edcdc1dae8 | |||
| 0b2467ed54 | |||
| c234b25ac7 | |||
| 492050b816 | |||
| a306407b67 | |||
| efd797a6f6 | |||
| a49e2ebbc5 | |||
| 154e53b911 | |||
| 98392fbe90 | |||
| fecf26318a | |||
| 2aa620e58f | |||
| b2760baa64 | |||
| cc1b774837 | |||
| 232f05be93 | |||
| 7cbce6806b | |||
| 0b66b2bbe7 | |||
| 87d4710bc4 | |||
| d456c799fd | |||
| 685dc24299 | |||
| ef58a8f205 | |||
| 748dfb1526 | |||
| a6f6533a28 | |||
| a2c25378a9 | |||
| 4a4442ad91 | |||
| f880f67049 | |||
| 0dabe6398f | |||
| 5308ea7e2a | |||
| d9d2e73228 | |||
| e1bc4c0730 | |||
| 074e4496ba | |||
| 64f0630fef | |||
| 47f843632f | |||
| db6f14748c | |||
| d82e3be8f7 | |||
| af997bf853 | |||
| 24bc8070c8 | |||
| 17dfaca43d | |||
| 9c9ad2f60a | |||
| b639bc9913 | |||
| 7829ad8fc9 | |||
| 23bf3d3959 | |||
| 14b23c2502 | |||
| e0ae3c7cb6 | |||
| ab1cca729f | |||
| 732dee5375 | |||
| 444e9563b3 | |||
| 4e5c4b8e01 | |||
| 66f9640787 | |||
| 53c3d48fe0 | |||
| 1e5334176e | |||
| 2f8d597f4e | |||
| afe2065d60 | |||
| f1a249b47a | |||
| 8d50b72d8f | |||
| fc2afa1eaa | |||
| 9767f4f9c3 | |||
| dc25370063 | |||
| bf138fb4ea | |||
| 5dc7265ad3 | |||
| 55d9988f3f | |||
| 8f56c9522e | |||
| ca5c732261 | |||
| 1754d54c18 | |||
| 12fb0404c1 | |||
| ad782c0000 | |||
| a0a41921fc | |||
| d85177cd3e | |||
| d2d5f1d7bd | |||
| 44f026220f | |||
| b59359f80f | |||
| 26b49c54af | |||
| 1e8ea59564 | |||
| 7776ebcedc | |||
| 63baa3b1c7 | |||
| 5dcbb8db38 | |||
| fbb5295f13 | |||
| 0407285c53 | |||
| f1be335486 | |||
| 55011d4bfd | |||
| de62ab97e9 | |||
| 45d52f7124 | |||
| 14bc7baeb7 | |||
| 660971d0b7 | |||
| 6d3973f965 | |||
| 064176d03d | |||
| 8f1a968d79 | |||
| fca83263f4 | |||
| d9bf4f7620 | |||
| afb59e7f98 | |||
| c5310d2e95 | |||
| f94fa5d5cf | |||
| 9e60eea93b | |||
| 6476f3277a | |||
| 24c172bb96 | |||
| 684823ebc4 | |||
| 901804d724 | |||
| 46046f9caa | |||
| 05531fbee4 | |||
| 78d5492d2c | |||
| bcdd1cee10 | |||
| caae349fdc | |||
| cc79cb803a | |||
| 8a7bc9ab4f | |||
| 6cc69c8d94 | |||
| 81527cdbdd | |||
| aef774a7a0 | |||
| 319d097198 | |||
| 5ef7d8fe62 | |||
| 1f64f9d016 | |||
| f1a5b6b668 | |||
| 5e9f5693a4 | |||
| 7fd88086f6 | |||
| 8f2e4d46ec | |||
| e267f6b88e | |||
| b551291840 | |||
| 36f7dda1ca | |||
| 710bbde09c | |||
| 70a097bf59 | |||
| 338d61f907 | |||
| 94f514ae5a | |||
| 37f666fbab | |||
| 2eaa10fc21 | |||
| f76dea0c03 | |||
| e7ca61e8b5 | |||
| 26144ca9df | |||
| b78ddc7ef7 | |||
| d52752a3fe | |||
| 497d078f50 | |||
| 13d7d6fe95 | |||
| 52112b0b8d | |||
| 1ef916a54b | |||
| 5f245d5950 | |||
| af833c8554 | |||
| 90c005bbec | |||
| 355a8898cf | |||
| ac8b4aa5f6 | |||
| 9fb07101dc | |||
| 2a8970368f | |||
| 7dc3fb98ab | |||
| c68dac879c | |||
| facd8ab109 | |||
| 70aefa75e2 | |||
| b49a79a15f | |||
| 7f55d34a7d | |||
| 9f4e47d7be | |||
| 56428cdb74 | |||
| 1d559ec703 | |||
| 5c37eca2c5 | |||
| 9ca92be0b8 | |||
| 1aca344688 | |||
| 1c49d0542d | |||
| 5404b2225a | |||
| 85042a74ee | |||
| b062d50bf1 | |||
| cdfe0d8f5a | |||
| 42bcf0870d | |||
| d7d9ad806a | |||
| e06cf07ab0 | |||
| ecfa1dcb6f | |||
| 9e1aa50518 | |||
| 66b86d43be | |||
| 7bf7cda467 | |||
| a209edb5ee | |||
| 8f5cab5608 | |||
| 32d521d79c | |||
| 6d565ff1fd | |||
| cdf63b553f | |||
| 1e7c0b4646 | |||
| dc17f287fe | |||
| d9e2a0cbf0 | |||
| 948ce36fa9 | |||
| 097ed84a87 | |||
| f276e83c8d | |||
| 7b4645f5f8 | |||
| 96aaab9366 | |||
| feffe1e81a | |||
| 98e8f941b7 | |||
| 6b4b00e462 | |||
| 51cb969446 | |||
| b878769ee0 | |||
| 583d431947 | |||
| d9b1995e95 | |||
| 9454d3e776 | |||
| ae22d742dc | |||
| 5ffb0842ac | |||
| b1b3a369ef | |||
| b64da108a9 | |||
| 190120dfd7 | |||
| bc0096365e | |||
| 1b3100ab9a | |||
| 0b591844b1 | |||
| 099b0a1f7c | |||
| 6a067d9f7e | |||
| 25de358c9a | |||
| a2d0bf8d3a | |||
| 1d208c49b5 | |||
| adf6fee6d1 | |||
| 2a08871fba | |||
| 441180a08a | |||
| 073829f26a | |||
| c040491615 | |||
| 40c5fe4c33 | |||
| 87fd288359 | |||
| c013068003 | |||
| 9f9faff90a | |||
| a11a13bdd1 | |||
| ad640245d8 | |||
| df87cf8002 | |||
| 27be429ca4 | |||
| 9d0bf065ee | |||
| 952fb94ff7 | |||
| 6d7a790183 | |||
| 0e8ca50d7a | |||
| d1833c7b94 | |||
| 521abf2e07 | |||
| 51092a6315 | |||
| 6f2a30dea2 | |||
| d3365f1b5b | |||
| f8558ed2b7 | |||
| 1cd6617ff6 | |||
| 52d1f7883b | |||
| 45d6c34015 | |||
| a9610b3607 | |||
| 5c859af24e | |||
| 513c18fdc4 | |||
| 9539ccc18b | |||
| eceeabaf2f | |||
| 652ef2b7fd | |||
| c2a79f0f7b | |||
| 08948079e9 | |||
| 0ce4c9b8fa | |||
| d04cc28f33 | |||
| 629980d15e | |||
| 55e3b840b3 | |||
| 2e1f1449bc | |||
| e720faf2d4 | |||
| 5181e78c1e | |||
| 2dd142fed0 | |||
| d3cc867bd4 | |||
| 864d5387dd | |||
| 29994aa2de | |||
| cd24e42b3f | |||
| e0e6efbfea | |||
| bb4726d520 | |||
| f5e8a8c75e | |||
| 23a34f732b | |||
| 5a2ef2590f | |||
| b23f81b686 | |||
| 3761336e6c | |||
| 9da152a9c8 | |||
| d45dd9c668 | |||
| 247f56249a | |||
| 2448f88e70 | |||
| d8c42918f4 | |||
| 909193a345 | |||
| 5a50c46c22 | |||
| 69dd570961 | |||
| 0d19a20901 | |||
| 0324712540 | |||
| 841d0bb893 | |||
| 65f9b2792c | |||
| 27695e2b24 | |||
| 427f484e01 | |||
| 6b030999cb | |||
| e92229f839 | |||
| 5bd63ff03d | |||
| 7cc8ba99f0 | |||
| 317d135b96 | |||
| c771b82a39 | |||
| 9f10ae466c | |||
| af362c2d05 | |||
| 428cde5dc2 | |||
| 2c1791752a | |||
| 071b83ba52 | |||
| 55a2474d7e | |||
| 3cc88b0aab | |||
| 6b2297b508 | |||
| 9c019126d4 | |||
| 19e96c6fef | |||
| 67ffdf7a78 | |||
| 3ba03c5a29 | |||
| f0c8b7f255 | |||
| 1ebfce4cac | |||
| f4d52f43d8 | |||
| 7025660e8b | |||
| 371fda1803 | |||
| aa56af1ecb | |||
| e8ac0e5bfd | |||
| d2e5aa79dd | |||
| ba0944af9b | |||
| bfb7a6e4c8 | |||
| e9009968fb | |||
| f1f93a593a | |||
| 959812ee13 | |||
| 325a49975a | |||
| cbb796bf31 | |||
| 9f23d17333 | |||
| 8e868823fd | |||
| e254a3f0c2 | |||
| 6e806848eb | |||
| 24de5127b1 | |||
| 58f84170b6 | |||
| 43ab6075f9 | |||
| e20f0ae5a3 | |||
| 987b6e069b | |||
| 18bb46163a | |||
| da4f0c65c3 | |||
| fcdf1cea70 | |||
| 40f359d159 | |||
| 080b0cea8b | |||
| 4c33818dde | |||
| 315bd556ac | |||
| dedc782222 | |||
| 1b104dd77b | |||
| e270a9784b | |||
| bcb6dfee71 | |||
| 8f581c0e66 | |||
| 19ba71e767 | |||
| f0c2c23ad1 | |||
| 70378925b0 | |||
| 66569295e5 | |||
| 338e0d72b4 | |||
| 02a6031d03 | |||
| ea82a6d19e | |||
| 10473285ba | |||
| 6886f77cbc | |||
| fcb669f803 | |||
| 49fd154829 | |||
| 8492c2845d | |||
| c618971758 | |||
| 01babb69d2 | |||
| 616adea4cc | |||
| 8bf4fe2eea | |||
| a502a41be3 | |||
| caead294f2 | |||
| b743bc1c1b | |||
| e69af7ab92 | |||
| 9ef7719734 | |||
| 0532acdf94 | |||
| 6a91d6a10a | |||
| 939d16d57c | |||
| acc9f66baf | |||
| 82e8dd0525 | |||
| 815947492c | |||
| 9682a62743 | |||
| 118cc0907d | |||
| 2d5efe424f | |||
| c8ce33d5bd | |||
| ac26b209a6 | |||
| d142ba1ab4 | |||
| 43e7323adb | |||
| a44f4c2fcb | |||
| e46a92c220 | |||
| d01d30ad5e | |||
| 9120e7987e | |||
| 2668da696c | |||
| 16ed117d71 | |||
| 4bb6b9589f | |||
| 698eddbb0c | |||
| f826f86842 | |||
| 76cd6797b7 | |||
| 965e91c702 | |||
| 9b8f36b758 | |||
| 1e1a8837f8 | |||
| 06639b8250 | |||
| bb6fba2183 | |||
| 422f065809 | |||
| 9349ee9002 | |||
| a0e4696f55 | |||
| 1e4d9cfa7e | |||
| 67c74e858b | |||
| a258790539 | |||
| f5e4a3ed85 | |||
| 24f9a0f2ae | |||
| d89624b03c | |||
| 4486043ae5 | |||
| 2f1f7f4c6a | |||
| bb48a7e520 | |||
| af5fb3d6e7 | |||
| a56bbbcf36 | |||
| 1ff24c7171 | |||
| a004ccfa1a | |||
| 53d8a49673 | |||
| 46b3e1d08c | |||
| efaa09dc23 | |||
| 651715abc9 | |||
| 4fcf9279fe | |||
| f993fd0cef | |||
| ba449ccce5 | |||
| 45dfdca6f9 | |||
| 0f24f49a44 | |||
| b21e481827 | |||
| 958301d880 | |||
| 4d91afb203 | |||
| c3c283a0b5 | |||
| 742f181595 | |||
| a52cfc8048 | |||
| ef67d17a84 | |||
| 64a8ad9711 | |||
| 3af1d5b722 | |||
| 7973c5abf8 | |||
| 6b7e14f11f | |||
| 1d9d1958db | |||
| 27916ce3db | |||
| 3481cf6fba | |||
| 6387933f8b | |||
| 9cbe02fd85 | |||
| 0558869d94 | |||
| a758cc8243 | |||
| 1541ff24b8 | |||
| b1f70b5b78 | |||
| 564030ffa4 | |||
| a20576bbd7 | |||
| 8a64f07121 | |||
| 42452b7f79 | |||
| cf0ef500ec | |||
| 17636a0c1c | |||
| 7428636071 | |||
| 6fb634c3e1 | |||
| 7a140b6912 | |||
| 566d449e53 | |||
| e01c337921 | |||
| ccf90df072 | |||
| f791b8c517 | |||
| 8d3a286577 | |||
| 6d7f9aacd6 | |||
| 4859984e33 | |||
| 54649851fa | |||
| 5bf0c64258 | |||
| 3d59021702 | |||
| 4b153dade8 | |||
| ef03de3981 | |||
| a16739f765 | |||
| 4e4e597fa5 | |||
| 0fbf34fb45 | |||
| d8e9b3c621 | |||
| 849f85549d | |||
| 8bf4a22876 | |||
| e77de8b12a | |||
| ea2fb38627 | |||
| c7eb0b7393 | |||
| dc8b7814c7 | |||
| bd054632e0 | |||
| 846cdd472d | |||
| d16954c34e | |||
| e13c6bca20 | |||
| 770e2ff7ed | |||
| c6c6e3324d | |||
| 9a60184bf6 | |||
| 56d3598020 | |||
| 6117cb2377 | |||
| f775f25379 | |||
| 232293792d | |||
| 48b954160e | |||
| 3b2d8d3949 | |||
| 2b3c7f211c | |||
| f1e3f0762b | |||
| 0fcd39c4c0 | |||
| 48ff5d4ce8 | |||
| bff5268ed4 | |||
| 2dd2756890 | |||
| 4bcd2fbb2e | |||
| 01af7b432d | |||
| af258efda4 | |||
| e4380ece5e | |||
| 0c5985374a | |||
| d253719ead | |||
| 33a84f17ac | |||
| b5b3ea95f8 | |||
| 96e83f95ee | |||
| 5e11a738c6 | |||
| d2eb866abe | |||
| b047eda1cb | |||
| a6f41e4cb0 | |||
| 25d1b7bdba | |||
| 45514ee32f | |||
| 11b38e1190 | |||
| 0a693d039a | |||
| 8fcc4afd2d | |||
| e4bbbe06fa | |||
| 94dd34fb4b | |||
| c0e7aab823 | |||
| 9f2e416bbd | |||
| 69af50cadb | |||
| fbba2828cd | |||
| dc8bbca7f1 | |||
| 84ca38ba95 | |||
| 35cbecabf9 | |||
| 2f3cffa517 | |||
| 470f454c53 | |||
| 5106f035df | |||
| e8202b915d | |||
| b4cb0e9bf5 | |||
| ee929a2eea | |||
| 4898234da8 | |||
| 14166264d6 | |||
| e4039cb9ae | |||
| fe2fb81047 | |||
| 0aae992f94 | |||
| 745cad5231 | |||
| 7ec89b4b6e | |||
| b495d80f56 | |||
| d096e183a8 | |||
| d340d50d49 | |||
| 8f4d6c48ce | |||
| f0cbcdc928 | |||
| 80da138d7f | |||
| 697ee19f71 | |||
| a49db89d7a | |||
| 0477d61599 | |||
| b2041a5ddf | |||
| 2c402e1ab5 | |||
| e38a81c5b6 | |||
| ce676ab70a | |||
| b7da69510c | |||
| a16d0b192e | |||
| c63d468d4c | |||
| 5e64882be9 | |||
| 72d2febccb | |||
| d1c4fb6364 | |||
| 612b24fa91 | |||
| 70a105c22c | |||
| 4802465f1a | |||
| 8aa4a74a7b | |||
| 4b8aa9caf1 | |||
| 9025e42afd | |||
| 6d4380a48c | |||
| 800e38bac6 | |||
| c837484015 | |||
| 01f3e33df9 | |||
| ea83c959ca | |||
| e8b274d349 | |||
| ea3fa06bc5 | |||
| 2e7ec3979a | |||
| 7bf64eaee0 | |||
| d80f00173f | |||
| aff725cba3 | |||
| 82675c018d | |||
| 82720c4759 | |||
| 2172f974e7 | |||
| 51e4aa7fc6 | |||
| aea7c85daa | |||
| 56c6b3f56c | |||
| 945fd022d6 | |||
| 63e98481ee | |||
| c522658d19 | |||
| 576c1116a6 | |||
| b3338d25f6 | |||
| 0f7e499323 | |||
| f751f5e25e | |||
| 93a94d177c | |||
| d89043b88e | |||
| 59cd6230a6 | |||
| 770bdcc108 | |||
| 9e6d8342de | |||
| 8d14824c65 | |||
| 210e150601 | |||
| ef0d4f8210 | |||
| 186b54aa3d | |||
| 21bc0f5ef8 | |||
| 906a4bb22d | |||
| c026f44a09 | |||
| 7dac02148b | |||
| 182c0cb61a | |||
| c2611f10ab | |||
| dcdeb8d1a6 | |||
| b0224485aa | |||
| d1e98c36d3 | |||
| 3556da0d02 | |||
| b9eacf4641 | |||
| 0ed6791a58 | |||
| 7555c86e7d | |||
| 574ec13e5b | |||
| f0871c8cfc | |||
| fdee577940 | |||
| c796f99d36 | |||
| c2dec5008b | |||
| c6a11770c5 | |||
| be7938ed38 | |||
| 80c9008798 | |||
| 1c736f4eb9 | |||
| cec03f39ae | |||
| 3387f23074 | |||
| 42531703cc | |||
| a59332f622 | |||
| cd2e9114e8 | |||
| 894c7d0f7e | |||
| 3b571e03ab | |||
| d2e8fae005 | |||
| d1f3a15470 | |||
| 325245f4b8 | |||
| a6c70992b0 | |||
| e16032e1f0 | |||
| bd65f64d05 | |||
| e27fad90de | |||
| f4ff5fc106 | |||
| d5e624391b | |||
| fb97822d8c | |||
| 21921cdaff | |||
| 65a9b97e46 | |||
| 221281688f | |||
| d924e3d6c4 | |||
| 38247e021a | |||
| 695cfa50b2 | |||
| 1faa7b536d | |||
| 7bdd68ddbe | |||
| a8101da956 | |||
| f7efa0aff7 | |||
| e127072b41 | |||
| 071f8a7b18 | |||
| 8145a26d07 | |||
| 93047e2a7a | |||
| 2cd73a8d97 | |||
| df0d225dbf | |||
| b3e500be32 | |||
| d6163fe570 | |||
| 2ccc1d1d8a | |||
| ca309e685e | |||
| 34539bc973 | |||
| 33cd3834ac | |||
| 91d53560d6 | |||
| 764d580100 | |||
| 6aaa11df30 | |||
| ad7ad48bd1 | |||
| b4a1ba9d3f | |||
| 1dcb04157c | |||
| 67db77452f | |||
| 7f4bd13f60 | |||
| e0466766d7 | |||
| e23da7a1de | |||
| 05dace6525 | |||
| 3148986184 | |||
| 19b130c45d | |||
| b0acfa1763 | |||
| 3646cf817c | |||
| 2847590d45 | |||
| 147274f165 | |||
| 0dc35784db | |||
| 713ac9e679 | |||
| 0a03596af1 | |||
| 3877545d8c | |||
| ce575718ef | |||
| 606402fff0 | |||
| 55ff0c143e | |||
| a3863d5834 | |||
| 8ff4a54356 | |||
| 3a26d853ba | |||
| 23bce59c66 | |||
| 53fadf5497 | |||
| 64cb21cb9f | |||
| 066df88a2c | |||
| 919b35295e | |||
| 0220404a91 | |||
| 0c4653085c | |||
| 22abaa287d | |||
| 5328972193 | |||
| bb03fe227b | |||
| 859cc703e7 | |||
| d648486bcd | |||
| da5f75615b | |||
| effb9d1e35 | |||
| a457157516 | |||
| 3e2672ec9f | |||
| 3b9fe27cbe | |||
| 6971e86847 | |||
| 101ae7644e | |||
| 58aa1751c3 | |||
| 1ca45fa3eb | |||
| 4e3fdf9f5d | |||
| 457c34c189 | |||
| d169fd859d | |||
| 773e2846e7 | |||
| 71d927333f | |||
| 9fdca9e29f | |||
| c89511b95e | |||
| abf6a9260a | |||
| 81384949e2 | |||
| 9e3a1942a3 | |||
| 11acbfebf7 | |||
| 96520520be | |||
| 6d664367bc | |||
| a12c420c59 | |||
| 0e1edc151d | |||
| adce176a35 | |||
| 99f685d20d | |||
| ac91083ceb | |||
| 67f086d1d4 | |||
| 3c127dc422 | |||
| 99a49122b8 | |||
| b6824ca716 | |||
| d04970e568 | |||
| 93d73aae4d | |||
| c9b2a0ae26 | |||
| 68a2ae4024 | |||
| e7ed3e2f72 | |||
| dea880dbf4 | |||
| ae1a2fa9c0 | |||
| 6512dcb233 | |||
| 2c6e59c4ca | |||
| 840fa1b5e8 | |||
| 9d94f792de | |||
| bd233c8cdb | |||
| e6f120df23 | |||
| c2dd681940 | |||
| e2bbee653e | |||
| e1ce7f9180 | |||
| e90cdbcf98 | |||
| e1c653b571 | |||
| 8575ecf3cb | |||
| 670aeec59a | |||
| 7ff5cf7820 | |||
| ee9917fe96 | |||
| 6b1f322220 | |||
| ecdc93f3ca | |||
| 25732d0895 | |||
| a9135ec39d | |||
| 000ced3795 | |||
| f4fa41487c | |||
| 6105d36fd7 | |||
| fd54f4f03d | |||
| 3ee42d9826 | |||
| c533060875 | |||
| 127c7e3b8c | |||
| 3cac4dff95 | |||
| 301d4c158b | |||
| 538f2839d1 | |||
| 8627a92ea8 | |||
| b8e74c88cf | |||
| 305ef3215d | |||
| d315ca4139 | |||
| b4a9b40d68 | |||
| 46cae36ef9 | |||
| 7f908d90af | |||
| d69dd6dc7a | |||
| 063ef9c2fc | |||
| 729f213c09 | |||
| 7111322526 | |||
| a046a951e9 | |||
| 3ac786c6af | |||
| 210b4c1fcd | |||
| c1f87414c5 | |||
| 2fa3ae85c9 | |||
| fd387beda1 | |||
| f547ef6d39 | |||
| 79e0f3e747 | |||
| 0189b923a2 | |||
| 4fb2526779 | |||
| 5761aa9400 | |||
| ddc885604d | |||
| 7de8db2960 | |||
| 5d552980a4 | |||
| 94816d0073 | |||
| 352d9afd64 | |||
| 87b6c30932 | |||
| fb599b5b83 | |||
| 26f5d385d5 | |||
| 0a9e9858f9 | |||
| 8f26ad4ba2 | |||
| bb0ac26ce1 | |||
| 9c05a16c9a | |||
| bbd2876afb | |||
| 3395b361fa | |||
| 0eb18e9711 | |||
| 0bb838bbaf | |||
| 1930ee3e5f | |||
| 3dc5f0bf76 | |||
| 78ca33a0c7 | |||
| d93398a361 | |||
| 5a73aa2502 | |||
| df2ee7ba87 | |||
| bce20d1f6b | |||
| abd568f9d8 | |||
| 2850719ea2 | |||
| e034afe107 | |||
| 01e43e5d4d | |||
| d7b2bba2e6 | |||
| 13bf636a34 | |||
| 14df326876 | |||
| e3d2df58b3 | |||
| 4612378593 | |||
| 26f8676b9b | |||
| f8a4ef6f51 | |||
| c08e975aa5 | |||
| 455d5f48c0 | |||
| 21998924d8 | |||
| 48447e2864 | |||
| 482372577e | |||
| 4e6acfa0d5 | |||
| 675ec99d33 | |||
| e45ff47361 | |||
| 63567526b0 | |||
| c8a3e51296 | |||
| 6bb4bdfa4c | |||
| 8ac8830478 | |||
| b2b7be6561 | |||
| 21c84bcd96 | |||
| f903a7aa64 | |||
| 5d039309b3 | |||
| ff62d22d31 | |||
| ffcdcbd5fa | |||
| a37cda0214 | |||
| 10bcda27a9 | |||
| 062d7e3487 | |||
| de53ff4aff | |||
| ceee5c8ffb | |||
| 9891d61206 | |||
| fe00b65d9a | |||
| 91258cc192 | |||
| ec7b21cd14 | |||
| de74498cd9 | |||
| 858230f452 | |||
| d6f4e5139f | |||
| 368f2d6907 | |||
| 16a6e010fd | |||
| 3e992309a5 | |||
| b6682ede61 | |||
| 203c249bf7 | |||
| 8c6d891694 | |||
| 431c89ece9 | |||
| 46e72a48ad | |||
| b9e192b78a | |||
| dce3d0c7bf | |||
| bd0acf2843 | |||
| c57f571920 | |||
| f094d94608 | |||
| 3121c30396 | |||
| 4a9dd6576f | |||
| 0644ebbaa2 | |||
| 19c437de6d | |||
| 0713b8c322 | |||
| c3d58947c3 | |||
| 79ad54f0c3 | |||
| 62f1df77d7 | |||
| f4a53cf609 | |||
| bd73cdab0d | |||
| 59c6fc966d | |||
| 35c4aae3d6 | |||
| b7a6a7da6a | |||
| 63259872d1 | |||
| 9ce2b8f9a7 | |||
| 8f8bf1f605 | |||
| e472cfcc53 | |||
| 6b627caf52 | |||
| 45f11d7b64 | |||
| 90349b384f | |||
| fc03a955e9 | |||
| 8d89c8be9e | |||
| a57fe89050 | |||
| 298fdb220d | |||
| 2a62b38530 | |||
| 714354caee | |||
| bca854baac | |||
| 4813125d31 | |||
| 6fc8cd3b29 | |||
| 6689c61ff4 | |||
| d9a77a7edc | |||
| acb37edc4f | |||
| 9b6b084d60 | |||
| fa32093906 | |||
| c4ba9d21dc | |||
| d9e9a7b05f | |||
| 83b41a7676 | |||
| 2730d8e76f | |||
| 2ff3bf5ab0 | |||
| 97f53d941c | |||
| 3c54ba3901 | |||
| f654767395 | |||
| 98eda6b35b | |||
| d386675efe | |||
| 9012bcf327 | |||
| c18e23477c | |||
| d7ec9d355f | |||
| 0498a4d35e | |||
| 2fba5c1f5e | |||
| be2028ba4a | |||
| 67455c66a1 | |||
| db4253d3bd | |||
| b4bc36c4a0 | |||
| ed0f3bc1b1 | |||
| f33d6a5211 | |||
| b859f1bdc3 | |||
| db22465c1d | |||
| 00e419e948 | |||
| 0211831858 | |||
| 75d8e5989f | |||
| 0149dded34 | |||
| 34e55c0034 | |||
| 4f4cadd756 | |||
| 0eef6d9d51 | |||
| 1b51ee8b87 | |||
| 946b3d5746 | |||
| a4c378dd00 | |||
| f590af510d | |||
| 074983b19c | |||
| 70fbc2b1ff | |||
| aa10068ca2 | |||
| 0fcd497796 | |||
| 07f80eb4e1 | |||
| d5675d5395 | |||
| da3b9568dd | |||
| 904cdfda32 | |||
| b68fbe3628 | |||
| 6d53ffdbf3 | |||
| dc10e56bab | |||
| e13d553aef | |||
| a7084b1051 | |||
| 8883ce1cef | |||
| 848a4a0fcc | |||
| 9992c4da99 | |||
| ab6622a8d6 | |||
| 3e60b18989 | |||
| 86ff35bf71 | |||
| 30d88cf547 | |||
| 17e2b4b3a5 | |||
| d8e2308adb | |||
| 83659db996 | |||
| ef43370927 | |||
| 64858e3e94 | |||
| 4bdd58bc83 | |||
| ba43582278 | |||
| 07d0bddb4f | |||
| 33ac3095dd | |||
| a774408753 | |||
| 5f5eebc4df | |||
| 46bbf95bae | |||
| db0f29f6d8 | |||
| 2f1bfb5945 | |||
| 4570345534 | |||
| 3c72865775 | |||
| b348abf5c3 | |||
| 2763f4096c | |||
| 87f3babe2c | |||
| 9469c4c79b | |||
| 483352f32b | |||
| 475a566e35 | |||
| d240077bc5 | |||
| 5d8a1c11b5 | |||
| 365a010732 | |||
| 804909e1d7 | |||
| d6290482cc | |||
| 703f1fdec2 | |||
| 28a516e8a5 | |||
| 6fc567f1ab | |||
| 0eb83fe462 | |||
| 5f40daabc2 | |||
| 30398083f4 | |||
| 9d6bb05ac6 | |||
| ce487d1e43 | |||
| 23729c2083 | |||
| 87e4ec6620 | |||
| 7b4f1cfb4a | |||
| 2cb974df40 | |||
| 70c63dd576 | |||
| 8fe421cba1 | |||
| e5fa4ee25d | |||
| bbc16e2c15 | |||
| d17534332f | |||
| 762cb00327 | |||
| 01f9cb4072 | |||
| baf1bd5b54 | |||
| c292b9e4ff | |||
| 7ffb9b3056 | |||
| 8559af5d48 | |||
| 6f59ebab27 | |||
| 53f6590b17 | |||
| d5ab883da2 | |||
| f6e52debb5 | |||
| c1146d27c6 | |||
| a5aaa3c9aa | |||
| 7d4e3688e1 | |||
| f134fbac5c | |||
| c2f88ffad6 | |||
| 11e6e07a72 | |||
| 8fa4f276f8 | |||
| f58d985789 | |||
| 4efe0d8696 | |||
| d90dfc8d1f | |||
| c24f4f230f | |||
| e5964d6dc4 | |||
| ed3f32b087 | |||
| 5543000c5b | |||
| 9f49ac0fda | |||
| 9479ea656b | |||
| 3058205963 | |||
| 80f0b5e736 | |||
| 0f51b6df4b | |||
| c6dc1376e4 | |||
| 8846ad967f | |||
| 8dfb1bc9db | |||
| 19f79be57b | |||
| 6fcaccc964 | |||
| b4c9744d6a | |||
| 18fed4e79a | |||
| 71b6e9bfe0 | |||
| a6e574ba9e | |||
| 3ed79cc229 | |||
| b9e81794e4 | |||
| cf5856ed48 | |||
| a1d4847d07 | |||
| 8933e21ef2 | |||
| 1393af4f9f | |||
| a925fb8111 | |||
| 61755f35c1 | |||
| c297b51a6e | |||
| f5dd5c1330 | |||
| 4a5daa2e91 | |||
| 50cdc0ac1e | |||
| 61743a3a00 | |||
| efb7b11d7f | |||
| 33ced1a7c2 | |||
| e6f4648924 | |||
| 12e443d61d | |||
| e53861bfb9 | |||
| 290badea95 | |||
| 5cbafaa014 | |||
| 0b6bb76259 | |||
| 199646ac2b | |||
| c1607300bb | |||
| 205e258d1a | |||
| f9a9235333 | |||
| ed05c83ee7 | |||
| 4dc9956a12 | |||
| bc17d6fc45 | |||
| ca28d81b36 | |||
| c71d8c0fc5 | |||
| ad11a90720 | |||
| 93b69583d3 | |||
| 2f162f0fc3 | |||
| a9cce5de3e | |||
| 6ccf10bbde | |||
| e84b3f4707 | |||
| beb670a939 | |||
| 5e189624b7 | |||
| b8d98dd6d0 | |||
| 7e6d0d6b4f | |||
| 23197ddbc3 | |||
| f2d59671b2 | |||
| b56d50912e | |||
| c2975b5f44 | |||
| a91bca64f8 | |||
| 13092d94b7 | |||
| 28a9f0826c | |||
| 6574fa4863 | |||
| aa4cf99e63 | |||
| abf0bd13ca | |||
| f300be00ae | |||
| 7f696edf13 | |||
| c3a36d9b19 | |||
| 9897fca794 | |||
| 13c2dd7115 | |||
| 94ad5b289f | |||
| 6086c344dc | |||
| 4b91d34d58 | |||
| af193e9fb3 | |||
| c37275efdc | |||
| c48a6196ad | |||
| d2adefe063 | |||
| 3b8f54d572 | |||
| 97f6d302fe | |||
| 1b7c554068 | |||
| a0e7ad493b | |||
| 177dd95686 | |||
| a606bbc7f1 | |||
| 4b77f4ef07 | |||
| e60e0761f5 | |||
| 05529d66ae | |||
| 3d1f1ffd7f | |||
| 0c74a14543 | |||
| cf50f44f4d | |||
| 6883619b14 | |||
| 8d6d2e9a2b | |||
| 82780a703b | |||
| 1db8392344 | |||
| bdbf6613ef | |||
| be476b3729 | |||
| 0a3e11b75e | |||
| aa231d25a7 | |||
| efb5c5eecd | |||
| 42ef85d3f6 | |||
| 70ddca33ba | |||
| 8e6c131b36 | |||
| cbe22763ee | |||
| fc00c1c009 | |||
| d6ebf5d1b6 | |||
| 13608d4d61 | |||
| c37a971947 | |||
| 2111fad92f | |||
| 391ab19b02 | |||
| 65620e0ae5 | |||
| 2c533fdc36 | |||
| 033cf3dca9 | |||
| 837b38b166 | |||
| 5464a5c630 | |||
| e5601a062e | |||
| 5796e7eae5 | |||
| 45d6bb58a4 | |||
| fc3608b381 | |||
| 7e716bfc4a | |||
| 4f279557a6 | |||
| ccbb3f069f | |||
| d77c6153e7 | |||
| 312330dbaa | |||
| f7d7afb7c1 | |||
| a217be1539 | |||
| 637993a793 | |||
| 4ebf0ad717 | |||
| dece86f61f | |||
| fb511351df | |||
| e41cfeca84 | |||
| a27e5e1652 | |||
| 38383671d7 | |||
| c8d82712da | |||
| ccba33169b | |||
| 4ad6905c13 | |||
| d608f0e9f0 | |||
| 6e198b3476 | |||
| 1dcc54c986 | |||
| 40a08bed4f | |||
| d4e52b64d8 | |||
| 308e6b697a | |||
| b10085cc63 | |||
| 9681c5a2bb | |||
| 55a13456d4 | |||
| a095645970 | |||
| 283c2b20d4 | |||
| e036cca1c9 | |||
| 1c4055419e | |||
| 3eab44001d | |||
| a50ba603ab | |||
| c053617066 | |||
| 3904a3f033 | |||
| 74994e9d11 | |||
| 907cd3dd01 | |||
| b34a374fa7 | |||
| 35eacf058e | |||
| 1a326c758b | |||
| 57387505d8 | |||
| 01a1e6f102 | |||
| 3f7cb8392e | |||
| 713e9dd4cc | |||
| 4c3f27193f | |||
| 6c954e71db | |||
| 0051a96376 | |||
| 7a98e92117 | |||
| ad247fe5cd | |||
| 4126ccf744 | |||
| 61a56ce120 | |||
| 6aed34cfb5 | |||
| 59a2858239 | |||
| 0185bdd8ca | |||
| da765ec53b | |||
| e39117467c | |||
| c834c3fae5 | |||
| e5016f814a | |||
| 6083a684d1 | |||
| d8cfdb3f34 | |||
| deba996769 | |||
| 857d68b0ce | |||
| 9d82ab9d1f | |||
| c82f39decd | |||
| 28c1ec830e | |||
| dd6e622206 | |||
| 3d2853274d | |||
| 336aba6f1f | |||
| ac51c9cce6 | |||
| 1c0596c233 | |||
| 0e09e779f9 | |||
| 3585021a18 | |||
| 87dcf65013 | |||
| 84582ceb03 | |||
| f05a2b86cd | |||
| 4320a1483b | |||
| a77387b549 | |||
| 79163b0755 | |||
| 70b23ab77f | |||
| 79fb86d8f7 | |||
| 95ba18cf4e | |||
| 9377d0f237 | |||
| 918c773a59 | |||
| 3aa63e3e9b | |||
| 2d89d33dd1 | |||
| af7330de88 | |||
| d82d6c701d | |||
| 4d36ef65b2 | |||
| 4955824c2d | |||
| 57623741f6 | |||
| 4e66224d6c | |||
| 01adfde199 | |||
| 49cb2421c7 | |||
| d714b90962 | |||
| 4a712dc612 | |||
| 7a31847f7d | |||
| 5ff1730e02 | |||
| a234fc11e5 | |||
| 9dacd89a02 | |||
| 2be33d8a77 | |||
| f38e88bc29 | |||
| 4d19d4f416 | |||
| ab829fd906 | |||
| 0b250b246d | |||
| 4b53d0e90c | |||
| 2f2768e7c3 | |||
| 30e01a7dba | |||
| 7f5497b863 | |||
| 5840f5e76f | |||
| ba827cdfff | |||
| 705c60d924 | |||
| f04b15692e | |||
| f18d2f2445 | |||
| 2b87ffece5 | |||
| 368bf037dd | |||
| 99b71278db | |||
| a3bba200c0 | |||
| 4f7b3f0c38 | |||
| 0d0222e91b | |||
| 259e265920 | |||
| 65d2e0eb8d | |||
| b92e643e97 | |||
| 3a6baab495 | |||
| ba9995a8de | |||
| e3d72ccd15 | |||
| 3c13e1e333 | |||
| 864528bb7b | |||
| 4a7e04add3 | |||
| 8630e8c7fc | |||
| dd1df64537 | |||
| d60ce35846 | |||
| b907c21f48 | |||
| 8364aa5f6f | |||
| ba3f1e7468 | |||
| 74c8de6372 | |||
| e3ee534c39 | |||
| 8ecbeb4945 | |||
| 960cdec0b5 | |||
| 4a9545affa | |||
| 6273a18de5 | |||
| a3c236598a | |||
| 936a79da7d | |||
| 94013216b6 | |||
| 22856844c6 | |||
| 357beb0570 | |||
| f173a67870 | |||
| 1692dda4b9 | |||
| 10c01110f3 | |||
| 6048d70232 | |||
| e64d0f83cd | |||
| 23441be474 | |||
| 951870b189 | |||
| bda39c5038 | |||
| 066a48c5fb | |||
| 7b4a2335eb | |||
| 451b780e08 | |||
| f5194a9d8e | |||
| 659b340716 | |||
| 1c1e878be7 | |||
| 87423f046e | |||
| 15a58eb383 | |||
| cdfc5a4d31 | |||
| ce046d2f03 | |||
| 6ce464aed8 | |||
| 7c847e62c3 | |||
| 38e8dc8540 | |||
| 6864053eee | |||
| 0ad3f61351 | |||
| d3980bdebc | |||
| 531c0d8e6b | |||
| a3dbe08c8b | |||
| e4b779c492 | |||
| a3b1d4a5e2 | |||
| 5a339a2a5b | |||
| 7b41ed7ec4 | |||
| 0ff4a91442 | |||
| 8592b9371a | |||
| 0c31ccd456 | |||
| 9370fea359 | |||
| 36381f3164 | |||
| 994c3f00e5 | |||
| 85e135beea | |||
| 17682cb0ce | |||
| 23cae7783d | |||
| 8635733d59 | |||
| 15d7ed2f0d | |||
| 0d3b18b17d | |||
| f4cdecebcf | |||
| c5af088b5f | |||
| f4f1c1eeed | |||
| 8dec92531b | |||
| dc76013126 | |||
| 23979ac648 | |||
| f809d3c81a | |||
| d0a64fe191 | |||
| 5de7271bcd | |||
| ad92181a26 | |||
| ff915534cd | |||
| b88aaaf1bc | |||
| 47f5e710c2 | |||
| 5afc29f65d | |||
| f667782df2 | |||
| 3f45c00779 | |||
| 17f79ea43e | |||
| fda5bc2bd2 | |||
| 5303ee2588 | |||
| cb726d87ce | |||
| fec1fec373 | |||
| 347d8f94e8 | |||
| 8b1b52a687 | |||
| 56bb8689d4 | |||
| 3c6752e765 | |||
| c06bb88756 | |||
| 8577df76c4 | |||
| 22982948cf | |||
| 8f8898b7b0 | |||
| 8176d2c058 | |||
| 389a2f7b4b | |||
| d19e42a723 | |||
| b9462bbe3c | |||
| 81b3395b41 | |||
| be903d67b8 | |||
| fb25a70f95 | |||
| 0fd215cb84 | |||
| 57c683f822 | |||
| a8deaf12f4 | |||
| c29eb63489 | |||
| 19ec7b2ad2 | |||
| 4c706f59d9 | |||
| dfded9595c | |||
| d0fac3fe1d | |||
| c07fb7b45c | |||
| 8ca1f4f371 | |||
| be66692f94 | |||
| 09d5475aa6 | |||
| 2ff266ca83 | |||
| 6d1622dc68 | |||
| fc0be88c0f | |||
| 3b2fcb3ef6 | |||
| d2c0a43498 | |||
| 933f51a8bf | |||
| 1d45c439b8 | |||
| 73614f228e | |||
| fd62868c17 | |||
| f0b65aa6b7 | |||
| 2d2bc25fd0 | |||
| b6e1042e8c | |||
| fd5e7f1466 | |||
| 0ec0f7561c | |||
| 3d6ffa282d | |||
| 4ca561acd3 | |||
| 13eac2552e | |||
| 5ef5d218c4 | |||
| 3fc9791747 | |||
| 7d997277a0 | |||
| a48f5d26fd | |||
| 9008bcbe54 | |||
| 2729a1549b | |||
| e32e3df3aa | |||
| 85e990e915 | |||
| 2b3550b61c | |||
| 100c059157 | |||
| ecdfcdbfa5 | |||
| 2e38de349c | |||
| 6ea050cc6c | |||
| 4b433c900e | |||
| dd2e6b7902 | |||
| e650db98f7 | |||
| 4a8d38c19a | |||
| f313ce2a96 | |||
| cd983245f1 | |||
| 8e04a8a022 | |||
| 7b782f6afa | |||
| ef9dfe3772 | |||
| 9317ec59b9 | |||
| c9083d04fa | |||
| 5a1abf6918 | |||
| 29b5dae6aa | |||
| bd2721dc7a | |||
| a5631e05cc | |||
| cd53a4b74c | |||
| 190ab90c0d | |||
| 82dd2d875e | |||
| 3a26ebef0b | |||
| 0b5b39d5e6 | |||
| 70f1e54068 | |||
| 1efddac3db | |||
| c5b25ba2a7 | |||
| a11e1c0858 | |||
| fdcdda2ed3 | |||
| 028bd9767c | |||
| 042003896f | |||
| 6dcd4fec7d | |||
| ef1b34dcd2 | |||
| f0797e27f3 | |||
| 16d3316f70 | |||
| a6974a6c83 | |||
| a1fbb434e8 | |||
| debd5346fb | |||
| f311f74001 | |||
| 1d2504d12e | |||
| 4454ae25c7 | |||
| 3582a44dff | |||
| 343200d229 | |||
| 58235bec17 | |||
| b89cc3417b | |||
| 9f8615c670 | |||
| 851a917b03 | |||
| e78acce652 | |||
| ddd1550a8b | |||
| e9297d9744 | |||
| 3e28fb33a3 | |||
| 9e25ea8666 | |||
| 67b4bd1031 | |||
| 9642bf637d | |||
| a455704148 | |||
| eebde08e65 | |||
| c4383b65e2 | |||
| eea46f268d | |||
| bf97f58edb | |||
| 8b7c71e20e | |||
| bd88e1743a | |||
| a2bddb7b40 | |||
| 3e274c3a57 | |||
| 1c01e94237 | |||
| d8d609b940 | |||
| 38b98de78e | |||
| 1aff37114a | |||
| fa12855d19 | |||
| 0ea979a262 | |||
| 1a76a3238c | |||
| cbb94405db | |||
| 2edd047178 | |||
| 2bc16e9947 | |||
| a48d235ef4 | |||
| bca7c682f1 | |||
| 2a7f3c015e | |||
| db90985f6a | |||
| a2bb4c3f13 | |||
| d066c7b124 | |||
| b2e533fbfc | |||
| 34e96aef60 | |||
| 2660ecfd4a | |||
| 3bdb3f9db7 | |||
| d90f684f16 | |||
| 804b08e0ca | |||
| f150676873 | |||
| f9d70aa9ef | |||
| 149df6d4db | |||
| 28e9f0db8c | |||
| 83970369e0 | |||
| 7dd4a2348f | |||
| a4cf378af8 | |||
| 9af8e10c20 | |||
| 0b527621a5 | |||
| 8fd224f841 | |||
| 420599f8e3 | |||
| 7fed6383c0 | |||
| ada4868d86 | |||
| a4fbe158d4 | |||
| 84afe4fb40 | |||
| d5ac11c87f | |||
| f6ad3ef221 | |||
| e9aee578a7 | |||
| 0d2521e30e | |||
| df3c54a4bf | |||
| ad71565e45 | |||
| ca1008571e | |||
| d856183c4b | |||
| dbe0897c7f | |||
| 5215b9cad2 | |||
| b435a8e01b | |||
| 32a2f95885 | |||
| bb969c3ccc | |||
| 043e25b602 | |||
| 96c559fbef | |||
| 887763e41c | |||
| 1d266aa834 | |||
| 59fc9aac0e | |||
| 8badd3740e | |||
| 1d69ec9ce1 | |||
| 9b685bace5 | |||
| 2eda15fc77 | |||
| c041a99f72 | |||
| b8d56190d1 | |||
| cb3315f9d3 | |||
| b979a8005f | |||
| 1353cb0d88 | |||
| 579e7c10c2 | |||
| 6c8bf9ec42 | |||
| 20e5ec18e1 | |||
| 5abefaed0a | |||
| 36df67f546 | |||
| 528c8c5e77 | |||
| 8a00c24012 | |||
| 4b5e8b8c40 | |||
| d1f1a1d056 | |||
| 47f5c436c8 | |||
| fbb4cbbe01 | |||
| e5d39a5f4c | |||
| c3b1c31d9b | |||
| 8cc3d05949 | |||
| 2149ccd985 | |||
| c6dd479229 | |||
| 8a51995cfa | |||
| 959d75edc8 | |||
| b020dd13fd | |||
| 184cf30cf7 | |||
| d124aee4d7 | |||
| 505ef82246 | |||
| e8517d8600 | |||
| 18ab9cbbdd | |||
| 29cb5058c0 | |||
| 9c70ca9da6 | |||
| 7bc08bb08c | |||
| e0d24f9da4 | |||
| e615ea3cf9 | |||
| 219a0e6352 | |||
| 0ef87227c1 | |||
| cce9609b9e | |||
| e76bc53d37 | |||
| 90ae4b7c0f | |||
| 023981acb9 | |||
| 1290f37750 | |||
| add3013234 | |||
| 4f1104125b | |||
| c6821e5dd1 | |||
| f1a4b95b8c | |||
| 5e0fc84f49 | |||
| 569374da2c | |||
| 482fb37b68 | |||
| 570eb4e1a7 | |||
| 48b472abb7 | |||
| 7b93e10f7a | |||
| 7570195b09 | |||
| d997be67fd | |||
| af8fda8a4a | |||
| 2ca673cca2 | |||
| 20886059fd | |||
| 18620ab5e0 | |||
| d11d061b2c | |||
| 4f3e71e5b7 | |||
| 045959e9c0 | |||
| 03fd2b8266 | |||
| de5c54db09 | |||
| cbb82647fb | |||
| e2cb41dc61 | |||
| 72986882f5 | |||
| a25260ea8a | |||
| f5e10b9945 | |||
| 52deb557d5 | |||
| 83038c0dab | |||
| 55f851093f | |||
| 92433532fe | |||
| 81e049bd47 | |||
| 0ce0a88fd6 | |||
| b0d511a860 | |||
| cc599333a8 | |||
| 9959972c7e | |||
| 842aeb888b | |||
| 22f6eb18a2 | |||
| d8bfd12d31 | |||
| 0a030c2bd9 | |||
| f5ce73646c | |||
| 8c33b4d742 | |||
| 983fa4630a | |||
| 40a0692a1c | |||
| ff63188cc2 | |||
| e6e3937fa9 | |||
| 168149ce9d | |||
| 0c5cbafcd8 | |||
| 213e3ba4af | |||
| 6ee54fb1f3 | |||
| d6f7aac1bb | |||
| 71b177918d | |||
| be2aeeb2fb | |||
| dc74473025 | |||
| d346038102 | |||
| cf066fd306 | |||
| bb787ba8ee | |||
| 308d87b12a | |||
| 372f3178f8 | |||
| 74f2864c74 | |||
| cde2f825d5 | |||
| 14a04020ff | |||
| 2e279684d7 | |||
| 7657fbb296 | |||
| 143f786d1a | |||
| bf4ddfef6d | |||
| 61569e09ed | |||
| a4126b66d4 | |||
| f82f3f10ab | |||
| 15b68fe694 | |||
| fb1f9aad6e | |||
| a1c440bd09 | |||
| f3a87d72b2 | |||
| b0a6b6427c | |||
| a6eb38ea7f | |||
| a2d9133ffc | |||
| c442c93a93 | |||
| ea8a85b19c | |||
| 3867e3857d | |||
| 6e7c0e15eb | |||
| fd3acf19fe | |||
| a8249c3469 | |||
| 3145bac8c6 | |||
| 99d82690ef | |||
| 1b9a722601 | |||
| 89506435fa | |||
| 0ddb9888fe | |||
| 9dd16f8b9c | |||
| 9a4ded2491 | |||
| c68ce288d0 | |||
| 5731534e8d | |||
| 9f43de62ab | |||
| ef8002a832 | |||
| e6e3bdee83 | |||
| 0e51becd58 | |||
| 46b2115bfb | |||
| 0626a44eb1 | |||
| 29e0af5ef0 | |||
| 09d1319e82 | |||
| ea0aea6508 | |||
| 6f711c32ba | |||
| 926ecc2dbe | |||
| 7c4339c195 | |||
| 755f161fc5 | |||
| 7bfea9087e | |||
| 11a7b3e8f8 | |||
| 112b0f166f | |||
| 69f3a7da6d | |||
| 9e2a8843a8 | |||
| 68a32a3486 | |||
| a4bc0a46e9 | |||
| 49baa9128d | |||
| f193b539ce | |||
| 35fd4dcf77 | |||
| 748be9a97d | |||
| 1a90c7f498 | |||
| be883131a7 | |||
| 658d35a1b0 | |||
| d387deb3e8 | |||
| 7206f5873c | |||
| 05e9c824d5 | |||
| 65a5311449 | |||
| d65279ac2a | |||
| 50c8d9181a | |||
| 08ba5d9dba | |||
| e118ac93a0 | |||
| 9ebeae6228 | |||
| 50b74629dd | |||
| 8d7559d9d0 | |||
| 56899716d6 | |||
| 443e6b73b2 | |||
| 5fdc3c093e | |||
| 1739998045 | |||
| bdc5850120 | |||
| 24ffb5b464 | |||
| 90fd92628d | |||
| 0c80f62de7 | |||
| 6f3dac6f58 | |||
| e252a100f7 | |||
| a5bfc1e30b | |||
| 9a57aca8d4 | |||
| 2aa95db5b9 | |||
| da2c9732da | |||
| cf66516a27 | |||
| 4079ee2200 | |||
| 5f961a392e | |||
| 540a99e71f | |||
| bb54743425 | |||
| 86f250a3f5 | |||
| 4834f56779 | |||
| 2dd87946fd | |||
| 355b8db915 | |||
| 13066bbf87 | |||
| 42a67731c4 | |||
| 3a31402b1e | |||
| d9846f3219 | |||
| e6e7e461c0 | |||
| 5dc4ea15bb | |||
| f790d0e91f | |||
| 49c17ad520 | |||
| fcf9e72202 | |||
| 5f85a1dcf0 | |||
| 01f547b93e | |||
| 3ff33e2f79 | |||
| e74e3e2b23 | |||
| 7d94e9c52f | |||
| 0a5a08c0fb | |||
| e699ab8614 | |||
| 6b2d3c3cfa | |||
| 396a2f1595 | |||
| 7640c658ee | |||
| 37d183efc0 | |||
| 06dd9f9b8c | |||
| 4e73b95fc0 | |||
| 4c35f7c632 | |||
| 15aa12847d | |||
| 699d84491f | |||
| d030e07879 | |||
| 1d41122372 | |||
| deecbbc55d | |||
| d9aedc1416 | |||
| fc928bb75a | |||
| 4659669a74 | |||
| 5aa274999c | |||
| 09665715ed | |||
| 0d7996409e | |||
| fe55cd6de8 | |||
| 7156f4439c | |||
| 0678e3fc2b | |||
| f85d733f9d | |||
| 2b772a5607 | |||
| 307ac564ca | |||
| cc30c5754f | |||
| d5f687b2fa | |||
| 35f9e35aa3 | |||
| b5ae424dbd | |||
| 677b864565 | |||
| b650ecbc23 | |||
| 94ddc5cb18 | |||
| cb3f82a4bf | |||
| f7556b827a | |||
| b0878fe609 | |||
| c911ca89a3 | |||
| a43b296c60 | |||
| d7de86966d | |||
| 0df5eb90ea | |||
| ede3c20880 | |||
| 5dc3f3626d | |||
| d596cf9cf3 | |||
| 1bd828603e | |||
| 03a6bf22a5 | |||
| 99933fac49 | |||
| 57491bf14c | |||
| 666acb314b | |||
| 4288d3cd01 | |||
| 1b5c3495c9 | |||
| 1e536cf7ca | |||
| 73fc4592ab | |||
| 8083fb5be7 | |||
| b9468dc917 | |||
| cd90bdd9bd | |||
| 0653680690 | |||
| 0095a59fc9 | |||
| ee92b3142d | |||
| ef0341c180 | |||
| 7f51569186 | |||
| 189d73dd87 | |||
| f244ef6f82 | |||
| bcc2b9a490 | |||
| 1dd05bac5c | |||
| a343d78724 | |||
| 57eef756c3 | |||
| 5349b90a48 | |||
| ac525db845 | |||
| a846418964 | |||
| 3f5914e094 | |||
| 517be463ae | |||
| dc13ce7a0a | |||
| c834713526 | |||
| 2633fbfc4b | |||
| e0babed29b | |||
| be446366fb | |||
| ea1d7d56d3 | |||
| 82d4e69da1 | |||
| f4cf84e72d | |||
| 8615a109ce | |||
| f8c68291d3 | |||
| 29a4ac329a | |||
| 5dad1688d4 | |||
| 98b0a3187e | |||
| 7794e53667 | |||
| bb0207d784 | |||
| a3c76c3274 | |||
| a48f362d28 | |||
| 489723723c | |||
| 182b08d8bc | |||
| 5554b8cfc9 | |||
| 3a1613253b | |||
| 6a76f50fa3 | |||
| 37fa82f138 | |||
| d315feae40 | |||
| 08dfbcfd5c | |||
| d339120145 | |||
| 80ea801204 | |||
| 191e8172f0 | |||
| b029b77784 | |||
| 4830b1afe7 | |||
| 25b9c3d0c1 | |||
| 4386ee32ce | |||
| a603dd55ff | |||
| 9228d13333 | |||
| bca959f372 | |||
| d6eff654be | |||
| e0fecdcaaa | |||
| 414a9edc6e | |||
| b84f6d100f | |||
| 801b7cb059 | |||
| 10dbb1bc9b | |||
| ca83629e4e | |||
| 5874e387a8 | |||
| d3c70e63b4 | |||
| 551f893ae9 | |||
| 692ab1085d | |||
| 94aeb5f52e | |||
| 9760a592b2 | |||
| f868d881d9 | |||
| 3d921e0e6f | |||
| a1a3f51be2 | |||
| c851e2c611 | |||
| ddf521eddc | |||
| 7142e3828f | |||
| 426429279b | |||
| 7460dd1b08 | |||
| 90a51d7897 | |||
| bc9c3de969 | |||
| 9f0e49917d | |||
| 779eb4b82b | |||
| 6e43757fed | |||
| a507d0aa75 | |||
| d4aa4e16a0 | |||
| d2b3615943 | |||
| 3437769ba0 | |||
| 3c65c946d4 | |||
| 16d5d5760c | |||
| 7a424ab4bf | |||
| 823782a33f | |||
| 321644de7c | |||
| a407d57639 | |||
| 0a6eff6a89 | |||
| 6fd857739c | |||
| 21f1e54cb9 | |||
| f8c95f1e90 | |||
| 8d9fb5109b | |||
| 20bcb68ad6 | |||
| 0617df9b5f | |||
| f24cb44781 | |||
| 2912d130c2 | |||
| 034935b2e1 | |||
| c1cdd3095b | |||
| dc7ed39fa7 | |||
| c8603092d3 | |||
| f065700ef9 | |||
| e54983694b | |||
| 3ed425d7ef | |||
| af8be56f17 | |||
| bf465eb2eb | |||
| 8ceb800def | |||
| b9e30f7604 | |||
| f93bfab824 | |||
| ff5277f4d7 | |||
| 6617985a4e | |||
| 54b8690781 | |||
| 959b768911 | |||
| a421e515be | |||
| 11365b42c7 | |||
| b42fcce014 | |||
| fc31a41473 | |||
| b3841653c6 | |||
| 32d3bde261 | |||
| c787a9bf74 | |||
| 4f72da0005 | |||
| 69d373db78 | |||
| 0803f1b2f1 | |||
| c080dcae8d | |||
| 997f6228de | |||
| 32e85d469b | |||
| 1518895e15 | |||
| 44172f701c | |||
| e1d5be570e | |||
| b5fece0381 | |||
| 1461903e0d | |||
| 8aa237ac23 | |||
| 5d014ba85f | |||
| 79edd2cf3b | |||
| 6425480d40 | |||
| 8448b94c0b | |||
| 9ee3d01f6e | |||
| 69e64e1ed2 | |||
| ba52ac9bcd | |||
| 43c375716a | |||
| ead8573aaf | |||
| 41175ec84c | |||
| b9fd3f9eb4 | |||
| 01a5946a2e | |||
| 2e605590a2 | |||
| 764ea95781 | |||
| 99fa5911bc | |||
| 2f676832bc | |||
| 94884ed04b | |||
| 2219ce2475 | |||
| 393d0eb138 | |||
| 51791e6a54 | |||
| d85f0c0bb2 | |||
| 0a95c470b0 | |||
| 7ebc1bb69e | |||
| 52096234e1 | |||
| 61c6a38f04 | |||
| f23a28e8f3 | |||
| 314f9e2686 | |||
| 2c7f94e285 | |||
| a375104826 | |||
| 62dd5f33b2 | |||
| 77a2c8bcf0 | |||
| d984cec3a1 | |||
| 5c7680ec84 | |||
| 18157bc331 | |||
| 27b66b2008 | |||
| f48c0c9e0a | |||
| b3f54124bd | |||
| eebc6c0419 | |||
| 144059b062 | |||
| 4017e4a96a | |||
| ed0568bb24 | |||
| 160e901381 | |||
| a9fac12969 | |||
| 3c90ed95af | |||
| 48d5002190 | |||
| 784e396cc7 | |||
| 8bd77be522 | |||
| ff44ebfd5b | |||
| a5471ce0a4 | |||
| 817ca82e4b | |||
| 68898dad1e | |||
| 024374e0ce | |||
| 647398d7c6 | |||
| dcefeac6e6 | |||
| 9fd8731480 | |||
| 056fa2a474 | |||
| fe5428dc49 | |||
| a371de080b | |||
| 0e0f70b288 | |||
| 7257aa3ed2 | |||
| 69e0c19767 | |||
| 8f14902290 | |||
| 2290e08053 | |||
| a7a3d1e194 | |||
| 9c3f1d11f0 | |||
| 764e3aa496 | |||
| e96cc656e9 | |||
| 48aa1e10d6 | |||
| 6556626055 | |||
| 59a38442ac | |||
| 0c7c5327c9 | |||
| 3e663d7307 | |||
| feeec34333 | |||
| bcf4842eb3 | |||
| b1ae44206f | |||
| 0ad71a67d4 | |||
| 334a7c1d59 | |||
| 8d874512b6 | |||
| 9e84f38a46 | |||
| 06c8442af6 | |||
| 0a4be3470e | |||
| 4bf5466f64 | |||
| 82caba6bbe | |||
| 8808159d55 | |||
| 2759239754 | |||
| d1c5599c8e | |||
| 6571d80540 | |||
| 2da6caeaa6 | |||
| e157238ce7 | |||
| 49446c7b32 | |||
| f105a8bb4b | |||
| 18807e06fe | |||
| 357cf72ab3 | |||
| 2376683128 | |||
| f667c028e6 | |||
| dbb6da8c47 | |||
| 4f068b51e5 | |||
| be4a4d7468 | |||
| eb15b13ca1 | |||
| 38a9e642df | |||
| b80efeb87d | |||
| 52efb8d7f4 | |||
| a6fa4a8bc2 | |||
| 1bed3cff21 | |||
| cdbb0722f6 | |||
| 2eb8a520d4 | |||
| 0a07ed14c2 | |||
| 604726c639 | |||
| eaccaa5028 | |||
| a5d94f5d09 | |||
| af1936be5d | |||
| e4186f4903 | |||
| 3ee0f8feb9 | |||
| 5c8c40afef | |||
| 5516d8df0b | |||
| fba9ac6db1 | |||
| de387c853f | |||
| 44da54ec7f | |||
| b6da74b6e2 | |||
| 7e9960f7f1 | |||
| c6872d4d86 | |||
| 5b706f3bdc | |||
| 80459b13e4 | |||
| 012fbc36b9 | |||
| 9dae98071f | |||
| 4c436b106d | |||
| 1c523df160 | |||
| 6aa722808e | |||
| c693e649e6 | |||
| 1976d64814 | |||
| 78e7c1c27b | |||
| a7ad6080f0 | |||
| 563f16dc2a | |||
| 43b406ad9b | |||
| dd48375bd6 | |||
| 678ce4109c | |||
| b313c881c0 | |||
| 2c501f3b28 | |||
| e725db4480 | |||
| 23703ff40f | |||
| fb11eb1190 | |||
| 061b75b343 | |||
| 6bf8f01af5 | |||
| bf54de4984 | |||
| eb1080910e | |||
| d237410d1d | |||
| 2486f13dae | |||
| 3fd0f23e48 | |||
| 991aba286f | |||
| a3846ba53b | |||
| 0423caeb8c | |||
| f1b0f67553 | |||
| 89fae876a4 | |||
| ac4930e233 | |||
| e358882f14 | |||
| 564a182259 | |||
| 9cd086a707 | |||
| c2adea1999 | |||
| 7d81d6b24e | |||
| 84944af142 | |||
| be25e6802d | |||
| 9b6a226da5 | |||
| eb5073a8a7 | |||
| beb84bdc21 | |||
| a20f1fa779 | |||
| 7445ffe1a6 | |||
| c225c59b0a | |||
| 37a6705556 | |||
| ed5738bfa4 | |||
| 5e14a83030 | |||
| 3b9b176f14 | |||
| 838f0fd71b | |||
| fd83e006ec | |||
| e51574c764 | |||
| 893a1bedbc | |||
| 4621a7b426 | |||
| 4808313e70 | |||
| 1fb9311d82 | |||
| dd34daed42 | |||
| 830428fb06 | |||
| 0ca7ab2cf8 | |||
| a0c39989f5 | |||
| 209d09ae13 | |||
| 0c030f7d9c | |||
| 2d800e99e2 | |||
| 0518ecca14 | |||
| 43b9027611 | |||
| 40ea4f4b46 | |||
| 4f624cb734 | |||
| b03013d6a7 | |||
| 9e79d144de | |||
| 40b3f60cde | |||
| 2b27d243ca | |||
| 2f07c46e20 | |||
| 3da1b540fd | |||
| 92dde81296 | |||
| e2368eb960 | |||
| 3e71a7cb94 | |||
| b92ad830ef | |||
| fc7adccd6f | |||
| 97bf718d0e | |||
| eb6360e1f8 | |||
| 41b1ba146a | |||
| 6450d0cbca | |||
| 4e281350a0 | |||
| e868dad61c | |||
| 4a3d36a176 | |||
| f3dddb6e63 | |||
| 3c3e3b123b | |||
| f02a9935c6 | |||
| 3711735ed9 | |||
| 2c802cc10a | |||
| e2e502970c | |||
| 404d601666 | |||
| 252fc40f78 | |||
| fb21a73b4e | |||
| 138a4c6d92 | |||
| c802bfccef | |||
| fae4959db3 | |||
| 8a67e5cfa2 | |||
| 617def3ef0 | |||
| 4f5aafe59f | |||
| a14383d6cc | |||
| fe1903fb56 | |||
| f0d27e5f91 | |||
| a852669dce | |||
| 3240894cd5 | |||
| 0b679167c9 | |||
| 5ac0a93fba | |||
| f7655d44a2 | |||
| 38e6bfb702 | |||
| 20ede6de59 | |||
| 5c70e6b9ab | |||
| 9bd3b88a58 | |||
| e60281c927 | |||
| 01136ecd51 | |||
| 1212523470 | |||
| 73105b2d4b | |||
| 06ab9cf70e | |||
| a3d4868ef4 | |||
| 815b03855b | |||
| 6750bbb1d7 | |||
| 43c8c4fa29 | |||
| 25b8a95987 | |||
| 99fcd6f816 | |||
| 513c6db0d5 | |||
| 1b7f01b7cc | |||
| a4f21aaa64 | |||
| 39bc2ba65c | |||
| d598f82722 | |||
| c22408cb2a | |||
| 8c3419ed48 | |||
| a98361ebbe | |||
| 999f0783c5 | |||
| 2a6dd2f243 | |||
| f3349221c6 | |||
| 65c7c468cc | |||
| ebdf636759 | |||
| 6b870714a9 | |||
| 708f3ccc3d | |||
| 84f4afa5f2 | |||
| 991e21a712 | |||
| 1a9cf7ab80 | |||
| 9501f1cb9e | |||
| d52cb3708b | |||
| fa8d374613 | |||
| 0d26b0dcc2 | |||
| a6287fd407 | |||
| 16bdf79d4c | |||
| 0b47844606 | |||
| 468759e6ab | |||
| f138774d78 | |||
| 068da3fe83 | |||
| 29b11db13f | |||
| dca6254b35 | |||
| cccec40e87 | |||
| b5d9d8e005 | |||
| 825c5b5282 | |||
| 25ca179389 | |||
| 8bd0592e3f | |||
| 06a0d1b86e | |||
| 41b2c2f3bb | |||
| 68faef5b84 | |||
| abf1309fb6 | |||
| d3891af36e | |||
| ed82194260 | |||
| 012e61b105 | |||
| d19d7321d9 | |||
| d4e82d441e | |||
| 2ceea21f76 | |||
| 12327da4fe | |||
| 52d403b0d1 | |||
| ccd35bffe7 | |||
| d1c93525ed | |||
| f0732426f2 | |||
| eedbd1e81c | |||
| 6005e7f27f | |||
| e779e64c95 | |||
| 3baaf99a6e | |||
| 4a86983a6f | |||
| c19d9d3452 | |||
| 79b6d89b3f | |||
| 535e8fd0f7 | |||
| a240abb6c8 | |||
| dd9d30e248 | |||
| 95535ce99e | |||
| df2e82da78 | |||
| a1daec66e2 | |||
| 657ee85136 | |||
| 965306b296 | |||
| 28086f6cb7 | |||
| 45bb010b28 | |||
| 90e0c74721 | |||
| a20341ac2a | |||
| 7635afcb52 | |||
| 74aee374a6 | |||
| 4f70766b95 | |||
| 9f58edd7a8 | |||
| 0f3a575a09 | |||
| ec74fb9ba2 | |||
| ff809f2271 | |||
| 41f52fdd6a | |||
| aa827790bd | |||
| 38e7bd5359 | |||
| 613ce3f976 | |||
| ce8a0a9fd5 | |||
| f59e4ba7c2 | |||
| df469bfdea | |||
| f4642953d4 | |||
| 88b0c30152 | |||
| 01183ad93b | |||
| 3f97af4c8c | |||
| 4c5cb2189b | |||
| 98ff6f990a | |||
| aaa81e00b7 | |||
| 1c44a96768 | |||
| 34fef456c8 | |||
| 7e81918f7b | |||
| 1b840a3db8 | |||
| 4e440fff62 | |||
| 81208acd6e | |||
| af3b5c23c8 | |||
| 9f208417df | |||
| f1ce139997 | |||
| f56dddfa73 | |||
| 2bf1979d4a | |||
| 89cab3cd39 | |||
| 6723d27e06 | |||
| 1c03025090 | |||
| 7a37a63b8e | |||
| 2b5fdad2c8 | |||
| a6f9ede849 | |||
| 78dac1bf4a | |||
| 795909c39e | |||
| 3c042a054d | |||
| 332d66c183 | |||
| de006947ac | |||
| 8a128e5d25 | |||
| 34cbacf424 | |||
| f2fa5e7c88 | |||
| 3b735cc34a | |||
| 238caa1492 | |||
| e5ebe345ad | |||
| 965608356f | |||
| bc1ce7b3ac | |||
| 6a3619c40f | |||
| 50fa9a44be | |||
| 4937a48bd9 | |||
| 987a81c4de | |||
| f5e0500df4 | |||
| 37924887e8 | |||
| 0db7c4131b | |||
| a56a974149 | |||
| af7f3768fa | |||
| 0865db564f | |||
| 2b020040b4 | |||
| 3ae5fcbd7e | |||
| ac4fc4026c | |||
| 9737f68315 | |||
| 2a8c26e390 | |||
| 3c395a82db | |||
| 15c6b9b165 | |||
| 147fd9de4e | |||
| 0bca771a88 | |||
| f4ff63e271 | |||
| bbcf4e8c1d | |||
| 2859357939 | |||
| 2cbb565d09 | |||
| 4d157d2849 | |||
| 64cd763e19 | |||
| c3f387da70 | |||
| 6ebeb3b4f7 | |||
| 2e18baa3c8 | |||
| c9e87a19a9 | |||
| 3b87e29e63 | |||
| e9a20cb985 | |||
| cfc5205569 | |||
| 17dfaa3aae | |||
| 0a3eba08d8 | |||
| add53e28c2 | |||
| f42f655ea9 | |||
| 8304fc1bef | |||
| ea84fee952 | |||
| d6fd52a0e5 | |||
| 0dde7dd88a | |||
| 1323e11701 | |||
| 7ac9944bb3 | |||
| e32e2fd6b8 | |||
| 6bd535bed0 | |||
| 524c88c402 | |||
| 1c096b101d | |||
| 583dc8dbca | |||
| befa4e7528 | |||
| bc6751237c | |||
| 6f3be31b60 | |||
| 8001d86751 | |||
| acca3e5a21 | |||
| d17f835e74 | |||
| ef1f8aa9a0 | |||
| a5ac6eb524 | |||
| ac84d95c6e | |||
| 785f52aa29 | |||
| 051195a991 | |||
| 90ca6675c2 | |||
| 0db9730a50 | |||
| ad2643d8af | |||
| 46840ee787 | |||
| 1e0282a238 | |||
| 259e4f5b38 | |||
| e44229c74e | |||
| 92827cc467 | |||
| dd3d5bc2d4 | |||
| b4c7b3309a | |||
| 010a66487d | |||
| aed35fd811 | |||
| 8d9838012d | |||
| 57d36ec582 | |||
| febc2a50cb | |||
| 74e128bd0f | |||
| c34d78f417 | |||
| 611bd0b2d3 | |||
| 267d38cf20 | |||
| 26456f13e1 | |||
| 2484c44db0 | |||
| a8a13fcbd8 | |||
| 0a193c5645 | |||
| 2450ee1723 | |||
| 67f1cdfcbe | |||
| 549d542a31 | |||
| 2cdb58fb85 | |||
| 68ab232364 | |||
| 871d61f3f9 | |||
| b08bd3746e | |||
| 39ab0b0aa3 | |||
| 5c70cacc13 | |||
| 8c2cd057b2 | |||
| fdc4133ac9 | |||
| 3833c021c0 | |||
| cafbd9461e | |||
| 6995ac1ed2 | |||
| 58e3dc3d52 | |||
| 3e49e79140 | |||
| dec8e81211 | |||
| 4188a5f3f3 | |||
| 31d124582b | |||
| 248e079301 | |||
| 5ce850570f | |||
| 648464a2da | |||
| 29f77003c0 | |||
| dfa38ed7a5 | |||
| 66c918c84b | |||
| 77f55da6c7 | |||
| d6af7cdcb2 | |||
| 0ad8047820 | |||
| 2115f578e9 | |||
| 511b4790d0 | |||
| d6491e21f9 | |||
| cff0f938e5 | |||
| 5418519637 | |||
| b023dbe7b8 | |||
| 8092cd9205 | |||
| 60da5bd338 | |||
| c9e64596a4 | |||
| 08bd5e8b37 | |||
| 125b743e5c | |||
| 9a3eb397f4 | |||
| 84c1f2bfd0 | |||
| d7ba49ea62 | |||
| e47557630e | |||
| f6a1e53f19 | |||
| a0a015f19f | |||
| e3bea63f94 |
+5
-1
@@ -1 +1,5 @@
|
||||
build
|
||||
build*/
|
||||
winbuild/
|
||||
win64build/
|
||||
openal-soft.kdev4
|
||||
.kdev4/
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
language: c
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
- os: linux
|
||||
dist: trusty
|
||||
env:
|
||||
- BUILD_ANDROID=true
|
||||
- os: osx
|
||||
sudo: required
|
||||
install:
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
|
||||
# Install pulseaudio, portaudio, ALSA, JACK dependencies for
|
||||
# corresponding backends.
|
||||
# Install Qt5 dependency for alsoft-config.
|
||||
sudo apt-get install -qq \
|
||||
libpulse-dev \
|
||||
portaudio19-dev \
|
||||
libasound2-dev \
|
||||
libjack-dev \
|
||||
qtbase5-dev
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
curl -o ~/android-ndk.zip https://dl.google.com/android/repository/android-ndk-r15-linux-x86_64.zip
|
||||
unzip -q ~/android-ndk.zip -d ~ \
|
||||
'android-ndk-r15/build/cmake/*' \
|
||||
'android-ndk-r15/build/core/toolchains/arm-linux-androideabi-*/*' \
|
||||
'android-ndk-r15/platforms/android-14/arch-arm/*' \
|
||||
'android-ndk-r15/source.properties' \
|
||||
'android-ndk-r15/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/*' \
|
||||
'android-ndk-r15/sources/cxx-stl/gnu-libstdc++/4.9/include/*' \
|
||||
'android-ndk-r15/sysroot/*' \
|
||||
'android-ndk-r15/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/*' \
|
||||
'android-ndk-r15/toolchains/llvm/prebuilt/linux-x86_64/*'
|
||||
fi
|
||||
script:
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && -z "${BUILD_ANDROID}" ]]; then
|
||||
cmake \
|
||||
-DALSOFT_REQUIRE_ALSA=ON \
|
||||
-DALSOFT_REQUIRE_OSS=ON \
|
||||
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_JACK=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "linux" && "${BUILD_ANDROID}" == "true" ]]; then
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r15/build/cmake/android.toolchain.cmake \
|
||||
-DALSOFT_REQUIRE_OPENSL=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
fi
|
||||
- >
|
||||
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
|
||||
cmake \
|
||||
-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||
-DALSOFT_EMBED_HRTF_DATA=YES \
|
||||
.
|
||||
fi
|
||||
- make -j2
|
||||
-363
@@ -1,363 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __MINGW64__
|
||||
#define _WIN32_IE 0x501
|
||||
#else
|
||||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef _WIN32_IE
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
typedef struct ConfigEntry {
|
||||
char *key;
|
||||
char *value;
|
||||
} ConfigEntry;
|
||||
|
||||
typedef struct ConfigBlock {
|
||||
char *name;
|
||||
ConfigEntry *entries;
|
||||
unsigned int entryCount;
|
||||
} ConfigBlock;
|
||||
|
||||
static ConfigBlock *cfgBlocks;
|
||||
static unsigned int cfgCount;
|
||||
|
||||
static char buffer[1024];
|
||||
|
||||
static void LoadConfigFromFile(FILE *f)
|
||||
{
|
||||
ConfigBlock *curBlock = cfgBlocks;
|
||||
ConfigEntry *ent;
|
||||
|
||||
while(fgets(buffer, sizeof(buffer), f))
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
if(!buffer[i] || buffer[i] == '#')
|
||||
continue;
|
||||
|
||||
memmove(buffer, buffer+i, strlen(buffer+i)+1);
|
||||
|
||||
if(buffer[0] == '[')
|
||||
{
|
||||
ConfigBlock *nextBlock;
|
||||
unsigned int i;
|
||||
|
||||
i = 1;
|
||||
while(buffer[i] && buffer[i] != ']')
|
||||
i++;
|
||||
|
||||
if(!buffer[i])
|
||||
{
|
||||
ERR("config parse error: bad line \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
buffer[i] = 0;
|
||||
|
||||
do {
|
||||
i++;
|
||||
if(buffer[i] && !isspace(buffer[i]))
|
||||
{
|
||||
if(buffer[i] != '#')
|
||||
WARN("config warning: extra data after block: \"%s\"\n", buffer+i);
|
||||
break;
|
||||
}
|
||||
} while(buffer[i]);
|
||||
|
||||
nextBlock = NULL;
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].name, buffer+1) == 0)
|
||||
{
|
||||
nextBlock = cfgBlocks+i;
|
||||
TRACE("found block '%s'\n", nextBlock->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!nextBlock)
|
||||
{
|
||||
nextBlock = realloc(cfgBlocks, (cfgCount+1)*sizeof(ConfigBlock));
|
||||
if(!nextBlock)
|
||||
{
|
||||
ERR("config parse error: error reallocating config blocks\n");
|
||||
continue;
|
||||
}
|
||||
cfgBlocks = nextBlock;
|
||||
nextBlock = cfgBlocks+cfgCount;
|
||||
cfgCount++;
|
||||
|
||||
nextBlock->name = strdup(buffer+1);
|
||||
nextBlock->entries = NULL;
|
||||
nextBlock->entryCount = 0;
|
||||
|
||||
TRACE("found new block '%s'\n", nextBlock->name);
|
||||
}
|
||||
curBlock = nextBlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Look for the option name */
|
||||
i = 0;
|
||||
while(buffer[i] && buffer[i] != '#' && buffer[i] != '=' &&
|
||||
!isspace(buffer[i]))
|
||||
i++;
|
||||
|
||||
if(!buffer[i] || buffer[i] == '#' || i == 0)
|
||||
{
|
||||
ERR("config parse error: malformed option line: \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Seperate the option */
|
||||
if(buffer[i] != '=')
|
||||
{
|
||||
buffer[i++] = 0;
|
||||
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
if(buffer[i] != '=')
|
||||
{
|
||||
ERR("config parse error: option without a value: \"%s\"\n", buffer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Find the start of the value */
|
||||
buffer[i++] = 0;
|
||||
while(isspace(buffer[i]))
|
||||
i++;
|
||||
|
||||
/* Check if we already have this option set */
|
||||
ent = curBlock->entries;
|
||||
while((unsigned int)(ent-curBlock->entries) < curBlock->entryCount)
|
||||
{
|
||||
if(strcasecmp(ent->key, buffer) == 0)
|
||||
break;
|
||||
ent++;
|
||||
}
|
||||
|
||||
if((unsigned int)(ent-curBlock->entries) >= curBlock->entryCount)
|
||||
{
|
||||
/* Allocate a new option entry */
|
||||
ent = realloc(curBlock->entries, (curBlock->entryCount+1)*sizeof(ConfigEntry));
|
||||
if(!ent)
|
||||
{
|
||||
ERR("config parse error: error reallocating config entries\n");
|
||||
continue;
|
||||
}
|
||||
curBlock->entries = ent;
|
||||
ent = curBlock->entries + curBlock->entryCount;
|
||||
curBlock->entryCount++;
|
||||
|
||||
ent->key = strdup(buffer);
|
||||
ent->value = NULL;
|
||||
}
|
||||
|
||||
/* Look for the end of the line (Null term, new-line, or #-symbol) and
|
||||
eat up the trailing whitespace */
|
||||
memmove(buffer, buffer+i, strlen(buffer+i)+1);
|
||||
|
||||
i = 0;
|
||||
while(buffer[i] && buffer[i] != '#' && buffer[i] != '\n')
|
||||
i++;
|
||||
do {
|
||||
i--;
|
||||
} while(i >= 0 && isspace(buffer[i]));
|
||||
buffer[++i] = 0;
|
||||
|
||||
free(ent->value);
|
||||
ent->value = strdup(buffer);
|
||||
|
||||
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
const char *str;
|
||||
FILE *f;
|
||||
|
||||
cfgBlocks = calloc(1, sizeof(ConfigBlock));
|
||||
cfgBlocks->name = strdup("general");
|
||||
cfgCount = 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(SHGetSpecialFolderPathA(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
size_t p = strlen(buffer);
|
||||
snprintf(buffer+p, sizeof(buffer)-p, "\\alsoft.ini");
|
||||
f = fopen(buffer, "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#else
|
||||
f = fopen("/etc/openal/alsoft.conf", "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
if((str=getenv("HOME")) != NULL && *str)
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "%s/.alsoftrc", str);
|
||||
f = fopen(buffer, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
f = fopen(str, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeALConfig(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
{
|
||||
unsigned int j;
|
||||
for(j = 0;j < cfgBlocks[i].entryCount;j++)
|
||||
{
|
||||
free(cfgBlocks[i].entries[j].key);
|
||||
free(cfgBlocks[i].entries[j].value);
|
||||
}
|
||||
free(cfgBlocks[i].entries);
|
||||
free(cfgBlocks[i].name);
|
||||
}
|
||||
free(cfgBlocks);
|
||||
cfgBlocks = NULL;
|
||||
cfgCount = 0;
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
if(!keyName)
|
||||
return def;
|
||||
|
||||
if(!blockName)
|
||||
blockName = "general";
|
||||
|
||||
for(i = 0;i < cfgCount;i++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].name, blockName) != 0)
|
||||
continue;
|
||||
|
||||
for(j = 0;j < cfgBlocks[i].entryCount;j++)
|
||||
{
|
||||
if(strcasecmp(cfgBlocks[i].entries[j].key, keyName) == 0)
|
||||
{
|
||||
TRACE("Found %s:%s = \"%s\"\n", blockName, keyName,
|
||||
cfgBlocks[i].entries[j].value);
|
||||
if(cfgBlocks[i].entries[j].value[0])
|
||||
return cfgBlocks[i].entries[j].value;
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("Key %s:%s not found\n", blockName, keyName);
|
||||
return def;
|
||||
}
|
||||
|
||||
int ConfigValueExists(const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
return !!val[0];
|
||||
}
|
||||
|
||||
int ConfigValueStr(const char *blockName, const char *keyName, const char **ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueInt(const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtol(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueUInt(const char *blockName, const char *keyName, unsigned int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtoul(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueFloat(const char *blockName, const char *keyName, float *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
#ifdef HAVE_STRTOF
|
||||
*ret = strtof(val, NULL);
|
||||
#else
|
||||
*ret = (float)strtod(val, NULL);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *blockName, const char *keyName, int def)
|
||||
{
|
||||
const char *val = GetConfigValue(blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return !!def;
|
||||
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
|
||||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALdedicatedState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
ALfloat gains[MAXCHANNELS];
|
||||
} ALdedicatedState;
|
||||
|
||||
|
||||
static ALvoid DedicatedDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALdedicatedState *state = (ALdedicatedState*)effect;
|
||||
free(state);
|
||||
}
|
||||
|
||||
static ALboolean DedicatedDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
|
||||
{
|
||||
(void)effect;
|
||||
(void)Device;
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid DedicatedUpdate(ALeffectState *effect, ALCdevice *device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALdedicatedState *state = (ALdedicatedState*)effect;
|
||||
const ALfloat *ChannelGain;
|
||||
ALfloat Gain;
|
||||
ALint pos;
|
||||
ALsizei s;
|
||||
|
||||
Gain = Slot->Gain * Slot->effect.Dedicated.Gain;
|
||||
for(s = 0;s < MAXCHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
|
||||
if(Slot->effect.type == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
{
|
||||
pos = aluCart2LUTpos(1.0f, 0.0f);
|
||||
ChannelGain = device->PanningLUT[pos];
|
||||
|
||||
for(s = 0;s < MAXCHANNELS;s++)
|
||||
state->gains[s] = ChannelGain[s] * Gain;
|
||||
}
|
||||
else if(Slot->effect.type == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
state->gains[LFE] = Gain;
|
||||
}
|
||||
|
||||
static ALvoid DedicatedProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS])
|
||||
{
|
||||
ALdedicatedState *state = (ALdedicatedState*)effect;
|
||||
const ALfloat *gains = state->gains;
|
||||
ALuint i, s;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sample;
|
||||
|
||||
sample = SamplesIn[i];
|
||||
for(s = 0;s < MAXCHANNELS;s++)
|
||||
SamplesOut[i][s] = sample * gains[s];
|
||||
}
|
||||
}
|
||||
|
||||
ALeffectState *DedicatedCreate(void)
|
||||
{
|
||||
ALdedicatedState *state;
|
||||
ALsizei s;
|
||||
|
||||
state = malloc(sizeof(*state));
|
||||
if(!state)
|
||||
return NULL;
|
||||
|
||||
state->state.Destroy = DedicatedDestroy;
|
||||
state->state.DeviceUpdate = DedicatedDeviceUpdate;
|
||||
state->state.Update = DedicatedUpdate;
|
||||
state->state.Process = DedicatedProcess;
|
||||
|
||||
for(s = 0;s < MAXCHANNELS;s++)
|
||||
state->gains[s] = 0.0f;
|
||||
|
||||
return &state->state;
|
||||
}
|
||||
-200
@@ -1,200 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALechoState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALuint BufferLength;
|
||||
|
||||
// The echo is two tap. The delay is the number of samples from before the
|
||||
// current offset
|
||||
struct {
|
||||
ALuint delay;
|
||||
} Tap[2];
|
||||
ALuint Offset;
|
||||
/* The panning gains for the two taps */
|
||||
ALfloat Gain[2][MAXCHANNELS];
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[2];
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid EchoDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
if(state)
|
||||
{
|
||||
free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
free(state);
|
||||
}
|
||||
}
|
||||
|
||||
static ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
ALuint maxlen, i;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = fastf2u(AL_ECHO_MAX_DELAY * Device->Frequency) + 1;
|
||||
maxlen += fastf2u(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1;
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp;
|
||||
|
||||
temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat));
|
||||
if(!temp)
|
||||
return AL_FALSE;
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
for(i = 0;i < state->BufferLength;i++)
|
||||
state->SampleBuffer[i] = 0.0f;
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid EchoUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
ALuint frequency = Device->Frequency;
|
||||
ALfloat dirGain, ambientGain;
|
||||
const ALfloat *ChannelGain;
|
||||
ALfloat lrpan, cw, g, gain;
|
||||
ALuint i, pos;
|
||||
|
||||
state->Tap[0].delay = fastf2u(Slot->effect.Echo.Delay * frequency) + 1;
|
||||
state->Tap[1].delay = fastf2u(Slot->effect.Echo.LRDelay * frequency);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
lrpan = Slot->effect.Echo.Spread;
|
||||
|
||||
state->FeedGain = Slot->effect.Echo.Feedback;
|
||||
|
||||
cw = aluCos(F_PI*2.0f * LOWPASSFREQREF / frequency);
|
||||
g = 1.0f - Slot->effect.Echo.Damping;
|
||||
state->iirFilter.coeff = lpCoeffCalc(g, cw);
|
||||
|
||||
gain = Slot->Gain;
|
||||
for(i = 0;i < MAXCHANNELS;i++)
|
||||
{
|
||||
state->Gain[0][i] = 0.0f;
|
||||
state->Gain[1][i] = 0.0f;
|
||||
}
|
||||
|
||||
ambientGain = aluSqrt(1.0f/Device->NumChan);
|
||||
dirGain = aluFabs(lrpan);
|
||||
|
||||
/* First tap panning */
|
||||
pos = aluCart2LUTpos(0.0f, ((lrpan>0.0f)?-1.0f:1.0f));
|
||||
ChannelGain = Device->PanningLUT[pos];
|
||||
|
||||
for(i = 0;i < Device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = Device->Speaker2Chan[i];
|
||||
state->Gain[0][chan] = lerp(ambientGain, ChannelGain[chan], dirGain) * gain;
|
||||
}
|
||||
|
||||
/* Second tap panning */
|
||||
pos = aluCart2LUTpos(0.0f, ((lrpan>0.0f)?1.0f:-1.0f));
|
||||
ChannelGain = Device->PanningLUT[pos];
|
||||
|
||||
for(i = 0;i < Device->NumChan;i++)
|
||||
{
|
||||
enum Channel chan = Device->Speaker2Chan[i];
|
||||
state->Gain[1][chan] = lerp(ambientGain, ChannelGain[chan], dirGain) * gain;
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid EchoProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS])
|
||||
{
|
||||
ALechoState *state = (ALechoState*)effect;
|
||||
const ALuint mask = state->BufferLength-1;
|
||||
const ALuint tap1 = state->Tap[0].delay;
|
||||
const ALuint tap2 = state->Tap[1].delay;
|
||||
ALuint offset = state->Offset;
|
||||
ALfloat smp;
|
||||
ALuint i, k;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++,offset++)
|
||||
{
|
||||
/* First tap */
|
||||
smp = state->SampleBuffer[(offset-tap1) & mask];
|
||||
for(k = 0;k < MAXCHANNELS;k++)
|
||||
SamplesOut[i][k] += smp * state->Gain[0][k];
|
||||
|
||||
/* Second tap */
|
||||
smp = state->SampleBuffer[(offset-tap2) & mask];
|
||||
for(k = 0;k < MAXCHANNELS;k++)
|
||||
SamplesOut[i][k] += smp * state->Gain[1][k];
|
||||
|
||||
// Apply damping and feedback gain to the second tap, and mix in the
|
||||
// new sample
|
||||
smp = lpFilter2P(&state->iirFilter, 0, smp+SamplesIn[i]);
|
||||
state->SampleBuffer[offset&mask] = smp * state->FeedGain;
|
||||
}
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
ALeffectState *EchoCreate(void)
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
state = malloc(sizeof(*state));
|
||||
if(!state)
|
||||
return NULL;
|
||||
|
||||
state->state.Destroy = EchoDestroy;
|
||||
state->state.DeviceUpdate = EchoDeviceUpdate;
|
||||
state->state.Update = EchoUpdate;
|
||||
state->state.Process = EchoProcess;
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer = NULL;
|
||||
|
||||
state->Tap[0].delay = 0;
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
|
||||
state->iirFilter.coeff = 0.0f;
|
||||
state->iirFilter.history[0] = 0.0f;
|
||||
state->iirFilter.history[1] = 0.0f;
|
||||
|
||||
return &state->state;
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
typedef struct ALmodulatorState {
|
||||
// Must be first in all effects!
|
||||
ALeffectState state;
|
||||
|
||||
enum {
|
||||
SINUSOID,
|
||||
SAWTOOTH,
|
||||
SQUARE
|
||||
} Waveform;
|
||||
|
||||
ALuint index;
|
||||
ALuint step;
|
||||
|
||||
ALfloat Gain[MAXCHANNELS];
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[1];
|
||||
} ALmodulatorState;
|
||||
|
||||
#define WAVEFORM_FRACBITS 16
|
||||
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
|
||||
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
|
||||
|
||||
static __inline ALfloat Sin(ALuint index)
|
||||
{
|
||||
return aluSin(index * (F_PI*2.0f / WAVEFORM_FRACONE));
|
||||
}
|
||||
|
||||
static __inline ALfloat Saw(ALuint index)
|
||||
{
|
||||
return index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
|
||||
}
|
||||
|
||||
static __inline ALfloat Square(ALuint index)
|
||||
{
|
||||
return ((index>>(WAVEFORM_FRACBITS-1))&1)*2.0f - 1.0f;
|
||||
}
|
||||
|
||||
|
||||
static __inline ALfloat hpFilter1P(FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
ALfloat *history = &iir->history[offset];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
|
||||
return input - output;
|
||||
}
|
||||
|
||||
|
||||
#define DECL_TEMPLATE(func) \
|
||||
static void Process##func(ALmodulatorState *state, ALuint SamplesToDo, \
|
||||
const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]) \
|
||||
{ \
|
||||
const ALuint step = state->step; \
|
||||
ALuint index = state->index; \
|
||||
ALfloat samp; \
|
||||
ALuint i, k; \
|
||||
\
|
||||
for(i = 0;i < SamplesToDo;i++) \
|
||||
{ \
|
||||
samp = SamplesIn[i]; \
|
||||
\
|
||||
index += step; \
|
||||
index &= WAVEFORM_FRACMASK; \
|
||||
samp *= func(index); \
|
||||
\
|
||||
samp = hpFilter1P(&state->iirFilter, 0, samp); \
|
||||
\
|
||||
for(k = 0;k < MAXCHANNELS;k++) \
|
||||
SamplesOut[i][k] += state->Gain[k] * samp; \
|
||||
} \
|
||||
state->index = index; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Sin)
|
||||
DECL_TEMPLATE(Saw)
|
||||
DECL_TEMPLATE(Square)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
static ALvoid ModulatorDestroy(ALeffectState *effect)
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
free(state);
|
||||
}
|
||||
|
||||
static ALboolean ModulatorDeviceUpdate(ALeffectState *effect, ALCdevice *Device)
|
||||
{
|
||||
return AL_TRUE;
|
||||
(void)effect;
|
||||
(void)Device;
|
||||
}
|
||||
|
||||
static ALvoid ModulatorUpdate(ALeffectState *effect, ALCdevice *Device, const ALeffectslot *Slot)
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
ALfloat gain, cw, a = 0.0f;
|
||||
ALuint index;
|
||||
|
||||
if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->Waveform = SINUSOID;
|
||||
else if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
|
||||
state->Waveform = SAWTOOTH;
|
||||
else if(Slot->effect.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)
|
||||
state->Waveform = SQUARE;
|
||||
|
||||
state->step = fastf2u(Slot->effect.Modulator.Frequency*WAVEFORM_FRACONE /
|
||||
Device->Frequency);
|
||||
if(state->step == 0) state->step = 1;
|
||||
|
||||
cw = aluCos(F_PI*2.0f * Slot->effect.Modulator.HighPassCutoff /
|
||||
Device->Frequency);
|
||||
a = (2.0f-cw) - aluSqrt(aluPow(2.0f-cw, 2.0f) - 1.0f);
|
||||
state->iirFilter.coeff = a;
|
||||
|
||||
gain = aluSqrt(1.0f/Device->NumChan);
|
||||
gain *= Slot->Gain;
|
||||
for(index = 0;index < MAXCHANNELS;index++)
|
||||
state->Gain[index] = 0.0f;
|
||||
for(index = 0;index < Device->NumChan;index++)
|
||||
{
|
||||
enum Channel chan = Device->Speaker2Chan[index];
|
||||
state->Gain[chan] = gain;
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ModulatorProcess(ALeffectState *effect, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS])
|
||||
{
|
||||
ALmodulatorState *state = (ALmodulatorState*)effect;
|
||||
|
||||
switch(state->Waveform)
|
||||
{
|
||||
case SINUSOID:
|
||||
ProcessSin(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
break;
|
||||
|
||||
case SAWTOOTH:
|
||||
ProcessSaw(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
break;
|
||||
|
||||
case SQUARE:
|
||||
ProcessSquare(state, SamplesToDo, SamplesIn, SamplesOut);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ALeffectState *ModulatorCreate(void)
|
||||
{
|
||||
ALmodulatorState *state;
|
||||
|
||||
state = malloc(sizeof(*state));
|
||||
if(!state)
|
||||
return NULL;
|
||||
|
||||
state->state.Destroy = ModulatorDestroy;
|
||||
state->state.DeviceUpdate = ModulatorDeviceUpdate;
|
||||
state->state.Update = ModulatorUpdate;
|
||||
state->state.Process = ModulatorProcess;
|
||||
|
||||
state->index = 0;
|
||||
state->step = 1;
|
||||
|
||||
state->iirFilter.coeff = 0.0f;
|
||||
state->iirFilter.history[0] = 0.0f;
|
||||
|
||||
return &state->state;
|
||||
}
|
||||
-1287
File diff suppressed because it is too large
Load Diff
-127
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
|
||||
struct RingBuffer {
|
||||
ALubyte *mem;
|
||||
|
||||
ALsizei frame_size;
|
||||
ALsizei length;
|
||||
ALint read_pos;
|
||||
ALint write_pos;
|
||||
|
||||
CRITICAL_SECTION cs;
|
||||
};
|
||||
|
||||
|
||||
RingBuffer *CreateRingBuffer(ALsizei frame_size, ALsizei length)
|
||||
{
|
||||
RingBuffer *ring = calloc(1, sizeof(*ring) + ((length+1) * frame_size));
|
||||
if(ring)
|
||||
{
|
||||
ring->mem = (ALubyte*)(ring+1);
|
||||
|
||||
ring->frame_size = frame_size;
|
||||
ring->length = length+1;
|
||||
ring->read_pos = 0;
|
||||
ring->write_pos = 0;
|
||||
|
||||
InitializeCriticalSection(&ring->cs);
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
|
||||
void DestroyRingBuffer(RingBuffer *ring)
|
||||
{
|
||||
if(ring)
|
||||
{
|
||||
DeleteCriticalSection(&ring->cs);
|
||||
free(ring);
|
||||
}
|
||||
}
|
||||
|
||||
ALsizei RingBufferSize(RingBuffer *ring)
|
||||
{
|
||||
ALsizei s;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
s = (ring->write_pos-ring->read_pos+ring->length) % ring->length;
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void WriteRingBuffer(RingBuffer *ring, const ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
|
||||
remain = (ring->read_pos-ring->write_pos-1+ring->length) % ring->length;
|
||||
if(remain < len) len = remain;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
remain = ring->length - ring->write_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
remain*ring->frame_size);
|
||||
memcpy(ring->mem, data+(remain*ring->frame_size),
|
||||
(len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(ring->mem+(ring->write_pos*ring->frame_size), data,
|
||||
len*ring->frame_size);
|
||||
|
||||
ring->write_pos += len;
|
||||
ring->write_pos %= ring->length;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
}
|
||||
|
||||
void ReadRingBuffer(RingBuffer *ring, ALubyte *data, ALsizei len)
|
||||
{
|
||||
int remain;
|
||||
|
||||
EnterCriticalSection(&ring->cs);
|
||||
|
||||
remain = ring->length - ring->read_pos;
|
||||
if(remain < len)
|
||||
{
|
||||
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), remain*ring->frame_size);
|
||||
memcpy(data+(remain*ring->frame_size), ring->mem, (len-remain)*ring->frame_size);
|
||||
}
|
||||
else
|
||||
memcpy(data, ring->mem+(ring->read_pos*ring->frame_size), len*ring->frame_size);
|
||||
|
||||
ring->read_pos += len;
|
||||
ring->read_pos %= ring->length;
|
||||
|
||||
LeaveCriticalSection(&ring->cs);
|
||||
}
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alThunk.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
typedef struct {
|
||||
ALuint (*func)(ALvoid*);
|
||||
ALvoid *ptr;
|
||||
HANDLE thread;
|
||||
} ThreadInfo;
|
||||
|
||||
static DWORD CALLBACK StarterFunc(void *ptr)
|
||||
{
|
||||
ThreadInfo *inf = (ThreadInfo*)ptr;
|
||||
ALint ret;
|
||||
|
||||
ret = inf->func(inf->ptr);
|
||||
ExitThread((DWORD)ret);
|
||||
|
||||
return (DWORD)ret;
|
||||
}
|
||||
|
||||
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
|
||||
{
|
||||
DWORD dummy;
|
||||
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
|
||||
if(!inf) return 0;
|
||||
|
||||
inf->func = func;
|
||||
inf->ptr = ptr;
|
||||
|
||||
inf->thread = CreateThread(NULL, 0, StarterFunc, inf, 0, &dummy);
|
||||
if(!inf->thread)
|
||||
{
|
||||
free(inf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
ALuint StopThread(ALvoid *thread)
|
||||
{
|
||||
ThreadInfo *inf = thread;
|
||||
DWORD ret = 0;
|
||||
|
||||
WaitForSingleObject(inf->thread, INFINITE);
|
||||
GetExitCodeThread(inf->thread, &ret);
|
||||
CloseHandle(inf->thread);
|
||||
|
||||
free(inf);
|
||||
|
||||
return (ALuint)ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
ALuint (*func)(ALvoid*);
|
||||
ALvoid *ptr;
|
||||
ALuint ret;
|
||||
pthread_t thread;
|
||||
} ThreadInfo;
|
||||
|
||||
static void *StarterFunc(void *ptr)
|
||||
{
|
||||
ThreadInfo *inf = (ThreadInfo*)ptr;
|
||||
inf->ret = inf->func(inf->ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALvoid *StartThread(ALuint (*func)(ALvoid*), ALvoid *ptr)
|
||||
{
|
||||
ThreadInfo *inf = malloc(sizeof(ThreadInfo));
|
||||
if(!inf) return NULL;
|
||||
|
||||
inf->func = func;
|
||||
inf->ptr = ptr;
|
||||
if(pthread_create(&inf->thread, NULL, StarterFunc, inf) != 0)
|
||||
{
|
||||
free(inf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inf;
|
||||
}
|
||||
|
||||
ALuint StopThread(ALvoid *thread)
|
||||
{
|
||||
ThreadInfo *inf = thread;
|
||||
ALuint ret;
|
||||
|
||||
pthread_join(inf->thread, NULL);
|
||||
ret = inf->ret;
|
||||
|
||||
free(inf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
+698
@@ -0,0 +1,698 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef __MINGW32__
|
||||
#define _WIN32_IE 0x501
|
||||
#else
|
||||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32_IE
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alconfig.h"
|
||||
#include "compat.h"
|
||||
#include "bool.h"
|
||||
|
||||
|
||||
typedef struct ConfigEntry {
|
||||
char *key;
|
||||
char *value;
|
||||
} ConfigEntry;
|
||||
|
||||
typedef struct ConfigBlock {
|
||||
ConfigEntry *entries;
|
||||
unsigned int entryCount;
|
||||
} ConfigBlock;
|
||||
static ConfigBlock cfgBlock;
|
||||
|
||||
|
||||
static char *lstrip(char *line)
|
||||
{
|
||||
while(isspace(line[0]))
|
||||
line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
static char *rstrip(char *line)
|
||||
{
|
||||
size_t len = strlen(line);
|
||||
while(len > 0 && isspace(line[len-1]))
|
||||
len--;
|
||||
line[len] = 0;
|
||||
return line;
|
||||
}
|
||||
|
||||
static int readline(FILE *f, char **output, size_t *maxlen)
|
||||
{
|
||||
size_t len = 0;
|
||||
int c;
|
||||
|
||||
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
|
||||
;
|
||||
if(c == EOF)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
if(len+1 >= *maxlen)
|
||||
{
|
||||
void *temp = NULL;
|
||||
size_t newmax;
|
||||
|
||||
newmax = (*maxlen ? (*maxlen)<<1 : 32);
|
||||
if(newmax > *maxlen)
|
||||
temp = realloc(*output, newmax);
|
||||
if(!temp)
|
||||
{
|
||||
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*output = temp;
|
||||
*maxlen = newmax;
|
||||
}
|
||||
(*output)[len++] = c;
|
||||
(*output)[len] = '\0';
|
||||
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static char *expdup(const char *str)
|
||||
{
|
||||
char *output = NULL;
|
||||
size_t maxlen = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while(*str != '\0')
|
||||
{
|
||||
const char *addstr;
|
||||
size_t addstrlen;
|
||||
size_t i;
|
||||
|
||||
if(str[0] != '$')
|
||||
{
|
||||
const char *next = strchr(str, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? (size_t)(next-str) : strlen(str);
|
||||
|
||||
str += addstrlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
str++;
|
||||
if(*str == '$')
|
||||
{
|
||||
const char *next = strchr(str+1, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? (size_t)(next-str) : strlen(str);
|
||||
|
||||
str += addstrlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasbraces;
|
||||
char envname[1024];
|
||||
size_t k = 0;
|
||||
|
||||
hasbraces = (*str == '{');
|
||||
if(hasbraces) str++;
|
||||
|
||||
while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1)
|
||||
envname[k++] = *(str++);
|
||||
envname[k++] = '\0';
|
||||
|
||||
if(hasbraces && *str != '}')
|
||||
continue;
|
||||
|
||||
if(hasbraces) str++;
|
||||
if((addstr=getenv(envname)) == NULL)
|
||||
continue;
|
||||
addstrlen = strlen(addstr);
|
||||
}
|
||||
}
|
||||
if(addstrlen == 0)
|
||||
continue;
|
||||
|
||||
if(addstrlen >= maxlen-len)
|
||||
{
|
||||
void *temp = NULL;
|
||||
size_t newmax;
|
||||
|
||||
newmax = len+addstrlen+1;
|
||||
if(newmax > maxlen)
|
||||
temp = realloc(output, newmax);
|
||||
if(!temp)
|
||||
{
|
||||
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, maxlen);
|
||||
return output;
|
||||
}
|
||||
|
||||
output = temp;
|
||||
maxlen = newmax;
|
||||
}
|
||||
|
||||
for(i = 0;i < addstrlen;i++)
|
||||
output[len++] = addstr[i];
|
||||
output[len] = '\0';
|
||||
}
|
||||
|
||||
return output ? output : calloc(1, 1);
|
||||
}
|
||||
|
||||
|
||||
static void LoadConfigFromFile(FILE *f)
|
||||
{
|
||||
char curSection[128] = "";
|
||||
char *buffer = NULL;
|
||||
size_t maxlen = 0;
|
||||
ConfigEntry *ent;
|
||||
|
||||
while(readline(f, &buffer, &maxlen))
|
||||
{
|
||||
char *line, *comment;
|
||||
char key[256] = "";
|
||||
char value[256] = "";
|
||||
|
||||
line = rstrip(lstrip(buffer));
|
||||
if(!line[0]) continue;
|
||||
|
||||
if(line[0] == '[')
|
||||
{
|
||||
char *section = line+1;
|
||||
char *endsection;
|
||||
|
||||
endsection = strchr(section, ']');
|
||||
if(!endsection || section == endsection)
|
||||
{
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
}
|
||||
if(endsection[1] != 0)
|
||||
{
|
||||
char *end = endsection+1;
|
||||
while(isspace(*end))
|
||||
++end;
|
||||
if(*end != 0 && *end != '#')
|
||||
{
|
||||
ERR("config parse error: bad line \"%s\"\n", line);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*endsection = 0;
|
||||
|
||||
if(strcasecmp(section, "general") == 0)
|
||||
curSection[0] = 0;
|
||||
else
|
||||
{
|
||||
size_t len, p = 0;
|
||||
do {
|
||||
char *nextp = strchr(section, '%');
|
||||
if(!nextp)
|
||||
{
|
||||
strncpy(curSection+p, section, sizeof(curSection)-1-p);
|
||||
break;
|
||||
}
|
||||
|
||||
len = nextp - section;
|
||||
if(len > sizeof(curSection)-1-p)
|
||||
len = sizeof(curSection)-1-p;
|
||||
strncpy(curSection+p, section, len);
|
||||
p += len;
|
||||
section = nextp;
|
||||
|
||||
if(((section[1] >= '0' && section[1] <= '9') ||
|
||||
(section[1] >= 'a' && section[1] <= 'f') ||
|
||||
(section[1] >= 'A' && section[1] <= 'F')) &&
|
||||
((section[2] >= '0' && section[2] <= '9') ||
|
||||
(section[2] >= 'a' && section[2] <= 'f') ||
|
||||
(section[2] >= 'A' && section[2] <= 'F')))
|
||||
{
|
||||
unsigned char b = 0;
|
||||
if(section[1] >= '0' && section[1] <= '9')
|
||||
b = (section[1]-'0') << 4;
|
||||
else if(section[1] >= 'a' && section[1] <= 'f')
|
||||
b = (section[1]-'a'+0xa) << 4;
|
||||
else if(section[1] >= 'A' && section[1] <= 'F')
|
||||
b = (section[1]-'A'+0x0a) << 4;
|
||||
if(section[2] >= '0' && section[2] <= '9')
|
||||
b |= (section[2]-'0');
|
||||
else if(section[2] >= 'a' && section[2] <= 'f')
|
||||
b |= (section[2]-'a'+0xa);
|
||||
else if(section[2] >= 'A' && section[2] <= 'F')
|
||||
b |= (section[2]-'A'+0x0a);
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p++] = b;
|
||||
section += 3;
|
||||
}
|
||||
else if(section[1] == '%')
|
||||
{
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p++] = '%';
|
||||
section += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p++] = '%';
|
||||
section += 1;
|
||||
}
|
||||
if(p < sizeof(curSection)-1)
|
||||
curSection[p] = 0;
|
||||
} while(p < sizeof(curSection)-1 && *section != 0);
|
||||
curSection[sizeof(curSection)-1] = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
comment = strchr(line, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
if(!line[0]) continue;
|
||||
|
||||
if(sscanf(line, "%255[^=] = \"%255[^\"]\"", key, value) == 2 ||
|
||||
sscanf(line, "%255[^=] = '%255[^\']'", key, value) == 2 ||
|
||||
sscanf(line, "%255[^=] = %255[^\n]", key, value) == 2)
|
||||
{
|
||||
/* sscanf doesn't handle '' or "" as empty values, so clip it
|
||||
* manually. */
|
||||
if(strcmp(value, "\"\"") == 0 || strcmp(value, "''") == 0)
|
||||
value[0] = 0;
|
||||
}
|
||||
else if(sscanf(line, "%255[^=] %255[=]", key, value) == 2)
|
||||
{
|
||||
/* Special case for 'key =' */
|
||||
value[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("config parse error: malformed option line: \"%s\"\n\n", line);
|
||||
continue;
|
||||
}
|
||||
rstrip(key);
|
||||
|
||||
if(curSection[0] != 0)
|
||||
{
|
||||
size_t len = strlen(curSection);
|
||||
memmove(&key[len+1], key, sizeof(key)-1-len);
|
||||
key[len] = '/';
|
||||
memcpy(key, curSection, len);
|
||||
}
|
||||
|
||||
/* Check if we already have this option set */
|
||||
ent = cfgBlock.entries;
|
||||
while((unsigned int)(ent-cfgBlock.entries) < cfgBlock.entryCount)
|
||||
{
|
||||
if(strcasecmp(ent->key, key) == 0)
|
||||
break;
|
||||
ent++;
|
||||
}
|
||||
|
||||
if((unsigned int)(ent-cfgBlock.entries) >= cfgBlock.entryCount)
|
||||
{
|
||||
/* Allocate a new option entry */
|
||||
ent = realloc(cfgBlock.entries, (cfgBlock.entryCount+1)*sizeof(ConfigEntry));
|
||||
if(!ent)
|
||||
{
|
||||
ERR("config parse error: error reallocating config entries\n");
|
||||
continue;
|
||||
}
|
||||
cfgBlock.entries = ent;
|
||||
ent = cfgBlock.entries + cfgBlock.entryCount;
|
||||
cfgBlock.entryCount++;
|
||||
|
||||
ent->key = strdup(key);
|
||||
ent->value = NULL;
|
||||
}
|
||||
|
||||
free(ent->value);
|
||||
ent->value = expdup(value);
|
||||
|
||||
TRACE("found '%s' = '%s'\n", ent->key, ent->value);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
al_string ppath = AL_STRING_INIT_STATIC();
|
||||
WCHAR buffer[MAX_PATH];
|
||||
const WCHAR *str;
|
||||
FILE *f;
|
||||
|
||||
if(SHGetSpecialFolderPathW(NULL, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
alstr_copy_wcstr(&filepath, buffer);
|
||||
alstr_append_cstr(&filepath, "\\alsoft.ini");
|
||||
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
|
||||
f = al_fopen(alstr_get_cstr(filepath), "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
alstr_reset(&filepath);
|
||||
}
|
||||
|
||||
GetProcBinary(&ppath, NULL);
|
||||
if(!alstr_empty(ppath))
|
||||
{
|
||||
alstr_append_cstr(&ppath, "\\alsoft.ini");
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(ppath));
|
||||
f = al_fopen(alstr_get_cstr(ppath), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
if((str=_wgetenv(L"ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
al_string filepath = AL_STRING_INIT_STATIC();
|
||||
alstr_copy_wcstr(&filepath, str);
|
||||
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(filepath));
|
||||
f = al_fopen(alstr_get_cstr(filepath), "rt");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
alstr_reset(&filepath);
|
||||
}
|
||||
|
||||
alstr_reset(&ppath);
|
||||
}
|
||||
#else
|
||||
void ReadALConfig(void)
|
||||
{
|
||||
al_string confpaths = AL_STRING_INIT_STATIC();
|
||||
al_string fname = AL_STRING_INIT_STATIC();
|
||||
const char *str;
|
||||
FILE *f;
|
||||
|
||||
str = "/etc/openal/alsoft.conf";
|
||||
|
||||
TRACE("Loading config %s...\n", str);
|
||||
f = al_fopen(str, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if(!(str=getenv("XDG_CONFIG_DIRS")) || str[0] == 0)
|
||||
str = "/etc/xdg";
|
||||
alstr_copy_cstr(&confpaths, str);
|
||||
/* Go through the list in reverse, since "the order of base directories
|
||||
* denotes their importance; the first directory listed is the most
|
||||
* important". Ergo, we need to load the settings from the later dirs
|
||||
* first so that the settings in the earlier dirs override them.
|
||||
*/
|
||||
while(!alstr_empty(confpaths))
|
||||
{
|
||||
char *next = strrchr(alstr_get_cstr(confpaths), ':');
|
||||
if(next)
|
||||
{
|
||||
size_t len = next - alstr_get_cstr(confpaths);
|
||||
alstr_copy_cstr(&fname, next+1);
|
||||
VECTOR_RESIZE(confpaths, len, len+1);
|
||||
VECTOR_ELEM(confpaths, len) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
alstr_reset(&fname);
|
||||
fname = confpaths;
|
||||
AL_STRING_INIT(confpaths);
|
||||
}
|
||||
|
||||
if(alstr_empty(fname) || VECTOR_FRONT(fname) != '/')
|
||||
WARN("Ignoring XDG config dir: %s\n", alstr_get_cstr(fname));
|
||||
else
|
||||
{
|
||||
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
|
||||
else alstr_append_cstr(&fname, "alsoft.conf");
|
||||
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
|
||||
f = al_fopen(alstr_get_cstr(fname), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
alstr_clear(&fname);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
if(mainBundle)
|
||||
{
|
||||
unsigned char fileName[PATH_MAX];
|
||||
CFURLRef configURL;
|
||||
|
||||
if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), NULL)) &&
|
||||
CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
|
||||
{
|
||||
f = al_fopen((const char*)fileName, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if((str=getenv("HOME")) != NULL && *str)
|
||||
{
|
||||
alstr_copy_cstr(&fname, str);
|
||||
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.alsoftrc");
|
||||
else alstr_append_cstr(&fname, ".alsoftrc");
|
||||
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
|
||||
f = al_fopen(alstr_get_cstr(fname), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
if((str=getenv("XDG_CONFIG_HOME")) != NULL && str[0] != 0)
|
||||
{
|
||||
alstr_copy_cstr(&fname, str);
|
||||
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
|
||||
else alstr_append_cstr(&fname, "alsoft.conf");
|
||||
}
|
||||
else
|
||||
{
|
||||
alstr_clear(&fname);
|
||||
if((str=getenv("HOME")) != NULL && str[0] != 0)
|
||||
{
|
||||
alstr_copy_cstr(&fname, str);
|
||||
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/.config/alsoft.conf");
|
||||
else alstr_append_cstr(&fname, ".config/alsoft.conf");
|
||||
}
|
||||
}
|
||||
if(!alstr_empty(fname))
|
||||
{
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
|
||||
f = al_fopen(alstr_get_cstr(fname), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
alstr_clear(&fname);
|
||||
GetProcBinary(&fname, NULL);
|
||||
if(!alstr_empty(fname))
|
||||
{
|
||||
if(VECTOR_BACK(fname) != '/') alstr_append_cstr(&fname, "/alsoft.conf");
|
||||
else alstr_append_cstr(&fname, "alsoft.conf");
|
||||
|
||||
TRACE("Loading config %s...\n", alstr_get_cstr(fname));
|
||||
f = al_fopen(alstr_get_cstr(fname), "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
if((str=getenv("ALSOFT_CONF")) != NULL && *str)
|
||||
{
|
||||
TRACE("Loading config %s...\n", str);
|
||||
f = al_fopen(str, "r");
|
||||
if(f)
|
||||
{
|
||||
LoadConfigFromFile(f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
alstr_reset(&fname);
|
||||
alstr_reset(&confpaths);
|
||||
}
|
||||
#endif
|
||||
|
||||
void FreeALConfig(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0;i < cfgBlock.entryCount;i++)
|
||||
{
|
||||
free(cfgBlock.entries[i].key);
|
||||
free(cfgBlock.entries[i].value);
|
||||
}
|
||||
free(cfgBlock.entries);
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def)
|
||||
{
|
||||
unsigned int i;
|
||||
char key[256];
|
||||
|
||||
if(!keyName)
|
||||
return def;
|
||||
|
||||
if(blockName && strcasecmp(blockName, "general") != 0)
|
||||
{
|
||||
if(devName)
|
||||
snprintf(key, sizeof(key), "%s/%s/%s", blockName, devName, keyName);
|
||||
else
|
||||
snprintf(key, sizeof(key), "%s/%s", blockName, keyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(devName)
|
||||
snprintf(key, sizeof(key), "%s/%s", devName, keyName);
|
||||
else
|
||||
{
|
||||
strncpy(key, keyName, sizeof(key)-1);
|
||||
key[sizeof(key)-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0;i < cfgBlock.entryCount;i++)
|
||||
{
|
||||
if(strcmp(cfgBlock.entries[i].key, key) == 0)
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key, cfgBlock.entries[i].value);
|
||||
if(cfgBlock.entries[i].value[0])
|
||||
return cfgBlock.entries[i].value;
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key);
|
||||
return def;
|
||||
}
|
||||
return GetConfigValue(NULL, blockName, keyName, def);
|
||||
}
|
||||
|
||||
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
return !!val[0];
|
||||
}
|
||||
|
||||
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtol(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = strtoul(val, NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
#ifdef HAVE_STRTOF
|
||||
*ret = strtof(val, NULL);
|
||||
#else
|
||||
*ret = (float)strtod(val, NULL);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
if(!val[0]) return 0;
|
||||
|
||||
*ret = (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
|
||||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def)
|
||||
{
|
||||
const char *val = GetConfigValue(devName, blockName, keyName, "");
|
||||
|
||||
if(!val[0]) return !!def;
|
||||
return (strcasecmp(val, "true") == 0 || strcasecmp(val, "yes") == 0 ||
|
||||
strcasecmp(val, "on") == 0 || atoi(val) != 0);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef ALCONFIG_H
|
||||
#define ALCONFIG_H
|
||||
|
||||
void ReadALConfig(void);
|
||||
void FreeALConfig(void);
|
||||
|
||||
int ConfigValueExists(const char *devName, const char *blockName, const char *keyName);
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName, const char *def);
|
||||
int GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, int def);
|
||||
|
||||
int ConfigValueStr(const char *devName, const char *blockName, const char *keyName, const char **ret);
|
||||
int ConfigValueInt(const char *devName, const char *blockName, const char *keyName, int *ret);
|
||||
int ConfigValueUInt(const char *devName, const char *blockName, const char *keyName, unsigned int *ret);
|
||||
int ConfigValueFloat(const char *devName, const char *blockName, const char *keyName, float *ret);
|
||||
int ConfigValueBool(const char *devName, const char *blockName, const char *keyName, int *ret);
|
||||
|
||||
#endif /* ALCONFIG_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef ALSTRING_H
|
||||
#define ALSTRING_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef char al_string_char_type;
|
||||
TYPEDEF_VECTOR(al_string_char_type, al_string)
|
||||
TYPEDEF_VECTOR(al_string, vector_al_string)
|
||||
|
||||
inline void alstr_reset(al_string *str)
|
||||
{ VECTOR_DEINIT(*str); }
|
||||
#define AL_STRING_INIT(_x) do { (_x) = (al_string)NULL; } while(0)
|
||||
#define AL_STRING_INIT_STATIC() ((al_string)NULL)
|
||||
#define AL_STRING_DEINIT(_x) alstr_reset(&(_x))
|
||||
|
||||
inline size_t alstr_length(const_al_string str)
|
||||
{ return VECTOR_SIZE(str); }
|
||||
|
||||
inline ALboolean alstr_empty(const_al_string str)
|
||||
{ return alstr_length(str) == 0; }
|
||||
|
||||
inline const al_string_char_type *alstr_get_cstr(const_al_string str)
|
||||
{ return str ? &VECTOR_FRONT(str) : ""; }
|
||||
|
||||
void alstr_clear(al_string *str);
|
||||
|
||||
int alstr_cmp(const_al_string str1, const_al_string str2);
|
||||
int alstr_cmp_cstr(const_al_string str1, const al_string_char_type *str2);
|
||||
|
||||
void alstr_copy(al_string *str, const_al_string from);
|
||||
void alstr_copy_cstr(al_string *str, const al_string_char_type *from);
|
||||
void alstr_copy_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
|
||||
|
||||
void alstr_append_char(al_string *str, const al_string_char_type c);
|
||||
void alstr_append_cstr(al_string *str, const al_string_char_type *from);
|
||||
void alstr_append_range(al_string *str, const al_string_char_type *from, const al_string_char_type *to);
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wchar.h>
|
||||
/* Windows-only methods to deal with WideChar strings. */
|
||||
void alstr_copy_wcstr(al_string *str, const wchar_t *from);
|
||||
void alstr_append_wcstr(al_string *str, const wchar_t *from);
|
||||
void alstr_copy_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
|
||||
void alstr_append_wrange(al_string *str, const wchar_t *from, const wchar_t *to);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* ALSTRING_H */
|
||||
+566
@@ -0,0 +1,566 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ambdec.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
static char *lstrip(char *line)
|
||||
{
|
||||
while(isspace(line[0]))
|
||||
line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
static char *rstrip(char *line)
|
||||
{
|
||||
size_t len = strlen(line);
|
||||
while(len > 0 && isspace(line[len-1]))
|
||||
len--;
|
||||
line[len] = 0;
|
||||
return line;
|
||||
}
|
||||
|
||||
static int readline(FILE *f, char **output, size_t *maxlen)
|
||||
{
|
||||
size_t len = 0;
|
||||
int c;
|
||||
|
||||
while((c=fgetc(f)) != EOF && (c == '\r' || c == '\n'))
|
||||
;
|
||||
if(c == EOF)
|
||||
return 0;
|
||||
|
||||
do {
|
||||
if(len+1 >= *maxlen)
|
||||
{
|
||||
void *temp = NULL;
|
||||
size_t newmax;
|
||||
|
||||
newmax = (*maxlen ? (*maxlen)<<1 : 32);
|
||||
if(newmax > *maxlen)
|
||||
temp = realloc(*output, newmax);
|
||||
if(!temp)
|
||||
{
|
||||
ERR("Failed to realloc "SZFMT" bytes from "SZFMT"!\n", newmax, *maxlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*output = temp;
|
||||
*maxlen = newmax;
|
||||
}
|
||||
(*output)[len++] = c;
|
||||
(*output)[len] = '\0';
|
||||
} while((c=fgetc(f)) != EOF && c != '\r' && c != '\n');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Custom strtok_r, since we can't rely on it existing. */
|
||||
static char *my_strtok_r(char *str, const char *delim, char **saveptr)
|
||||
{
|
||||
/* Sanity check and update internal pointer. */
|
||||
if(!saveptr || !delim) return NULL;
|
||||
if(str) *saveptr = str;
|
||||
str = *saveptr;
|
||||
|
||||
/* Nothing more to do with this string. */
|
||||
if(!str) return NULL;
|
||||
|
||||
/* Find the first non-delimiter character. */
|
||||
while(*str != '\0' && strchr(delim, *str) != NULL)
|
||||
str++;
|
||||
if(*str == '\0')
|
||||
{
|
||||
/* End of string. */
|
||||
*saveptr = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the next delimiter character. */
|
||||
*saveptr = strpbrk(str, delim);
|
||||
if(*saveptr) *((*saveptr)++) = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *read_int(ALint *num, const char *line, int base)
|
||||
{
|
||||
char *end;
|
||||
*num = strtol(line, &end, base);
|
||||
if(end && *end != '\0')
|
||||
end = lstrip(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
static char *read_uint(ALuint *num, const char *line, int base)
|
||||
{
|
||||
char *end;
|
||||
*num = strtoul(line, &end, base);
|
||||
if(end && *end != '\0')
|
||||
end = lstrip(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
static char *read_float(ALfloat *num, const char *line)
|
||||
{
|
||||
char *end;
|
||||
#ifdef HAVE_STRTOF
|
||||
*num = strtof(line, &end);
|
||||
#else
|
||||
*num = (ALfloat)strtod(line, &end);
|
||||
#endif
|
||||
if(end && *end != '\0')
|
||||
end = lstrip(end);
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
char *read_clipped_line(FILE *f, char **buffer, size_t *maxlen)
|
||||
{
|
||||
while(readline(f, buffer, maxlen))
|
||||
{
|
||||
char *line, *comment;
|
||||
|
||||
line = lstrip(*buffer);
|
||||
comment = strchr(line, '#');
|
||||
if(comment) *(comment++) = 0;
|
||||
|
||||
line = rstrip(line);
|
||||
if(line[0]) return line;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int load_ambdec_speakers(AmbDecConf *conf, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
|
||||
{
|
||||
ALsizei cur = 0;
|
||||
while(cur < conf->NumSpeakers)
|
||||
{
|
||||
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(!cmd)
|
||||
{
|
||||
char *line = read_clipped_line(f, buffer, maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
return 0;
|
||||
}
|
||||
cmd = my_strtok_r(line, " \t", saveptr);
|
||||
}
|
||||
|
||||
if(strcmp(cmd, "add_spkr") == 0)
|
||||
{
|
||||
const char *name = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *dist = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *az = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *elev = my_strtok_r(NULL, " \t", saveptr);
|
||||
const char *conn = my_strtok_r(NULL, " \t", saveptr);
|
||||
|
||||
if(!name) WARN("Name not specified for speaker %u\n", cur+1);
|
||||
else alstr_copy_cstr(&conf->Speakers[cur].Name, name);
|
||||
if(!dist) WARN("Distance not specified for speaker %u\n", cur+1);
|
||||
else read_float(&conf->Speakers[cur].Distance, dist);
|
||||
if(!az) WARN("Azimuth not specified for speaker %u\n", cur+1);
|
||||
else read_float(&conf->Speakers[cur].Azimuth, az);
|
||||
if(!elev) WARN("Elevation not specified for speaker %u\n", cur+1);
|
||||
else read_float(&conf->Speakers[cur].Elevation, elev);
|
||||
if(!conn) TRACE("Connection not specified for speaker %u\n", cur+1);
|
||||
else alstr_copy_cstr(&conf->Speakers[cur].Connection, conn);
|
||||
|
||||
cur++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected speakers command: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(cmd)
|
||||
{
|
||||
ERR("Unexpected junk on line: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int load_ambdec_matrix(ALfloat *gains, ALfloat (*matrix)[MAX_AMBI_COEFFS], ALsizei maxrow, FILE *f, char **buffer, size_t *maxlen, char **saveptr)
|
||||
{
|
||||
int gotgains = 0;
|
||||
ALsizei cur = 0;
|
||||
while(cur < maxrow)
|
||||
{
|
||||
const char *cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(!cmd)
|
||||
{
|
||||
char *line = read_clipped_line(f, buffer, maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
return 0;
|
||||
}
|
||||
cmd = my_strtok_r(line, " \t", saveptr);
|
||||
}
|
||||
|
||||
if(strcmp(cmd, "order_gain") == 0)
|
||||
{
|
||||
ALuint curgain = 0;
|
||||
char *line;
|
||||
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
|
||||
{
|
||||
ALfloat value;
|
||||
line = read_float(&value, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk on gain %u: %s\n", curgain+1, line);
|
||||
return 0;
|
||||
}
|
||||
if(curgain < MAX_AMBI_ORDER+1)
|
||||
gains[curgain] = value;
|
||||
curgain++;
|
||||
}
|
||||
while(curgain < MAX_AMBI_ORDER+1)
|
||||
gains[curgain++] = 0.0f;
|
||||
gotgains = 1;
|
||||
}
|
||||
else if(strcmp(cmd, "add_row") == 0)
|
||||
{
|
||||
ALuint curidx = 0;
|
||||
char *line;
|
||||
while((line=my_strtok_r(NULL, " \t", saveptr)) != NULL)
|
||||
{
|
||||
ALfloat value;
|
||||
line = read_float(&value, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk on matrix element %ux%u: %s\n", cur, curidx, line);
|
||||
return 0;
|
||||
}
|
||||
if(curidx < MAX_AMBI_COEFFS)
|
||||
matrix[cur][curidx] = value;
|
||||
curidx++;
|
||||
}
|
||||
while(curidx < MAX_AMBI_COEFFS)
|
||||
matrix[cur][curidx++] = 0.0f;
|
||||
cur++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected speakers command: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmd = my_strtok_r(NULL, " \t", saveptr);
|
||||
if(cmd)
|
||||
{
|
||||
ERR("Unexpected junk on line: %s\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!gotgains)
|
||||
{
|
||||
ERR("Matrix order_gain not specified\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ambdec_init(AmbDecConf *conf)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
AL_STRING_INIT(conf->Description);
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
AL_STRING_INIT(conf->Speakers[i].Name);
|
||||
AL_STRING_INIT(conf->Speakers[i].Connection);
|
||||
}
|
||||
}
|
||||
|
||||
void ambdec_deinit(AmbDecConf *conf)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
alstr_reset(&conf->Description);
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
alstr_reset(&conf->Speakers[i].Name);
|
||||
alstr_reset(&conf->Speakers[i].Connection);
|
||||
}
|
||||
memset(conf, 0, sizeof(*conf));
|
||||
}
|
||||
|
||||
int ambdec_load(AmbDecConf *conf, const char *fname)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
size_t maxlen = 0;
|
||||
char *line;
|
||||
FILE *f;
|
||||
|
||||
f = al_fopen(fname, "r");
|
||||
if(!f)
|
||||
{
|
||||
ERR("Failed to open: %s\n", fname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while((line=read_clipped_line(f, &buffer, &maxlen)) != NULL)
|
||||
{
|
||||
char *saveptr;
|
||||
char *command;
|
||||
|
||||
command = my_strtok_r(line, "/ \t", &saveptr);
|
||||
if(!command)
|
||||
{
|
||||
ERR("Malformed line: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(strcmp(command, "description") == 0)
|
||||
{
|
||||
char *value = my_strtok_r(NULL, "", &saveptr);
|
||||
alstr_copy_cstr(&conf->Description, lstrip(value));
|
||||
}
|
||||
else if(strcmp(command, "version") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_uint(&conf->Version, line, 10);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after version: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->Version != 3)
|
||||
{
|
||||
ERR("Unsupported version: %u\n", conf->Version);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "dec") == 0)
|
||||
{
|
||||
const char *dec = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(dec, "chan_mask") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_uint(&conf->ChanMask, line, 16);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after mask: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(dec, "freq_bands") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_uint(&conf->FreqBands, line, 10);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after freq_bands: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->FreqBands != 1 && conf->FreqBands != 2)
|
||||
{
|
||||
ERR("Invalid freq_bands value: %u\n", conf->FreqBands);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(dec, "speakers") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_int(&conf->NumSpeakers, line, 10);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after speakers: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->NumSpeakers > MAX_OUTPUT_CHANNELS)
|
||||
{
|
||||
ERR("Unsupported speaker count: %u\n", conf->NumSpeakers);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(dec, "coeff_scale") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, " \t", &saveptr);
|
||||
if(strcmp(line, "n3d") == 0)
|
||||
conf->CoeffScale = ADS_N3D;
|
||||
else if(strcmp(line, "sn3d") == 0)
|
||||
conf->CoeffScale = ADS_SN3D;
|
||||
else if(strcmp(line, "fuma") == 0)
|
||||
conf->CoeffScale = ADS_FuMa;
|
||||
else
|
||||
{
|
||||
ERR("Unsupported coeff scale: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected /dec option: %s\n", dec);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "opt") == 0)
|
||||
{
|
||||
const char *opt = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(opt, "xover_freq") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_float(&conf->XOverFreq, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after xover_freq: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(opt, "xover_ratio") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "", &saveptr);
|
||||
line = read_float(&conf->XOverRatio, line);
|
||||
if(line && *line != '\0')
|
||||
{
|
||||
ERR("Extra junk after xover_ratio: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(opt, "input_scale") == 0 || strcmp(opt, "nfeff_comp") == 0 ||
|
||||
strcmp(opt, "delay_comp") == 0 || strcmp(opt, "level_comp") == 0)
|
||||
{
|
||||
/* Unused */
|
||||
my_strtok_r(NULL, " \t", &saveptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected /opt option: %s\n", opt);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "speakers") == 0)
|
||||
{
|
||||
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(value, "{") != 0)
|
||||
{
|
||||
ERR("Expected { after %s command, got %s\n", command, value);
|
||||
goto fail;
|
||||
}
|
||||
if(!load_ambdec_speakers(conf, f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(!value)
|
||||
{
|
||||
line = read_clipped_line(f, &buffer, &maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
goto fail;
|
||||
}
|
||||
value = my_strtok_r(line, "/ \t", &saveptr);
|
||||
}
|
||||
if(strcmp(value, "}") != 0)
|
||||
{
|
||||
ERR("Expected } after speaker definitions, got %s\n", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "lfmatrix") == 0 || strcmp(command, "hfmatrix") == 0 ||
|
||||
strcmp(command, "matrix") == 0)
|
||||
{
|
||||
const char *value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(strcmp(value, "{") != 0)
|
||||
{
|
||||
ERR("Expected { after %s command, got %s\n", command, value);
|
||||
goto fail;
|
||||
}
|
||||
if(conf->FreqBands == 1)
|
||||
{
|
||||
if(strcmp(command, "matrix") != 0)
|
||||
{
|
||||
ERR("Unexpected \"%s\" type for a single-band decoder\n", command);
|
||||
goto fail;
|
||||
}
|
||||
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
|
||||
f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strcmp(command, "lfmatrix") == 0)
|
||||
{
|
||||
if(!load_ambdec_matrix(conf->LFOrderGain, conf->LFMatrix, conf->NumSpeakers,
|
||||
f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
}
|
||||
else if(strcmp(command, "hfmatrix") == 0)
|
||||
{
|
||||
if(!load_ambdec_matrix(conf->HFOrderGain, conf->HFMatrix, conf->NumSpeakers,
|
||||
f, &buffer, &maxlen, &saveptr))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected \"%s\" type for a dual-band decoder\n", command);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
value = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(!value)
|
||||
{
|
||||
line = read_clipped_line(f, &buffer, &maxlen);
|
||||
if(!line)
|
||||
{
|
||||
ERR("Unexpected end of file\n");
|
||||
goto fail;
|
||||
}
|
||||
value = my_strtok_r(line, "/ \t", &saveptr);
|
||||
}
|
||||
if(strcmp(value, "}") != 0)
|
||||
{
|
||||
ERR("Expected } after matrix definitions, got %s\n", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if(strcmp(command, "end") == 0)
|
||||
{
|
||||
line = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(line)
|
||||
{
|
||||
ERR("Unexpected junk on end: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
free(buffer);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Unexpected command: %s\n", command);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
line = my_strtok_r(NULL, "/ \t", &saveptr);
|
||||
if(line)
|
||||
{
|
||||
ERR("Unexpected junk on line: %s\n", line);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
ERR("Unexpected end of file\n");
|
||||
|
||||
fail:
|
||||
fclose(f);
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef AMBDEC_H
|
||||
#define AMBDEC_H
|
||||
|
||||
#include "alstring.h"
|
||||
#include "alMain.h"
|
||||
|
||||
/* Helpers to read .ambdec configuration files. */
|
||||
|
||||
enum AmbDecScaleType {
|
||||
ADS_N3D,
|
||||
ADS_SN3D,
|
||||
ADS_FuMa,
|
||||
};
|
||||
typedef struct AmbDecConf {
|
||||
al_string Description;
|
||||
ALuint Version; /* Must be 3 */
|
||||
|
||||
ALuint ChanMask;
|
||||
ALuint FreqBands; /* Must be 1 or 2 */
|
||||
ALsizei NumSpeakers;
|
||||
enum AmbDecScaleType CoeffScale;
|
||||
|
||||
ALfloat XOverFreq;
|
||||
ALfloat XOverRatio;
|
||||
|
||||
struct {
|
||||
al_string Name;
|
||||
ALfloat Distance;
|
||||
ALfloat Azimuth;
|
||||
ALfloat Elevation;
|
||||
al_string Connection;
|
||||
} Speakers[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Unused when FreqBands == 1 */
|
||||
ALfloat LFOrderGain[MAX_AMBI_ORDER+1];
|
||||
ALfloat LFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
|
||||
ALfloat HFOrderGain[MAX_AMBI_ORDER+1];
|
||||
ALfloat HFMatrix[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
} AmbDecConf;
|
||||
|
||||
void ambdec_init(AmbDecConf *conf);
|
||||
void ambdec_deinit(AmbDecConf *conf);
|
||||
int ambdec_load(AmbDecConf *conf, const char *fname);
|
||||
|
||||
#endif /* AMBDEC_H */
|
||||
+771
-605
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
extern inline ALuint64 GetDeviceClockTime(ALCdevice *device);
|
||||
extern inline void ALCdevice_Lock(ALCdevice *device);
|
||||
extern inline void ALCdevice_Unlock(ALCdevice *device);
|
||||
extern inline ClockLatency GetClockLatency(ALCdevice *device);
|
||||
|
||||
/* Base ALCbackend method implementations. */
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device)
|
||||
{
|
||||
int ret = almtx_init(&self->mMutex, almtx_recursive);
|
||||
assert(ret == althrd_success);
|
||||
self->mDevice = device;
|
||||
}
|
||||
|
||||
void ALCbackend_Destruct(ALCbackend *self)
|
||||
{
|
||||
almtx_destroy(&self->mMutex);
|
||||
}
|
||||
|
||||
ALCboolean ALCbackend_reset(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend* UNUSED(self), void* UNUSED(buffer), ALCuint UNUSED(samples))
|
||||
{
|
||||
return ALC_INVALID_DEVICE;
|
||||
}
|
||||
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend* UNUSED(self))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ClockLatency ALCbackend_getClockLatency(ALCbackend *self)
|
||||
{
|
||||
ALCdevice *device = self->mDevice;
|
||||
ALuint refcount;
|
||||
ClockLatency ret;
|
||||
|
||||
do {
|
||||
while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
|
||||
althrd_yield();
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
ATOMIC_THREAD_FENCE(almemory_order_acquire);
|
||||
} while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
|
||||
|
||||
/* NOTE: The device will generally have about all but one periods filled at
|
||||
* any given time during playback. Without a more accurate measurement from
|
||||
* the output, this is an okay approximation.
|
||||
*/
|
||||
ret.Latency = device->UpdateSize * DEVICE_CLOCK_RES / device->Frequency *
|
||||
maxu(device->NumUpdates-1, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ALCbackend_lock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_lock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
void ALCbackend_unlock(ALCbackend *self)
|
||||
{
|
||||
int ret = almtx_unlock(&self->mMutex);
|
||||
assert(ret == althrd_success);
|
||||
}
|
||||
|
||||
|
||||
/* Base ALCbackendFactory method implementations. */
|
||||
void ALCbackendFactory_deinit(ALCbackendFactory* UNUSED(self))
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#ifndef AL_BACKENDS_BASE_H
|
||||
#define AL_BACKENDS_BASE_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "threads.h"
|
||||
#include "alstring.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ClockLatency {
|
||||
ALint64 ClockTime;
|
||||
ALint64 Latency;
|
||||
} ClockLatency;
|
||||
|
||||
/* Helper to get the current clock time from the device's ClockBase, and
|
||||
* SamplesDone converted from the sample rate.
|
||||
*/
|
||||
inline ALuint64 GetDeviceClockTime(ALCdevice *device)
|
||||
{
|
||||
return device->ClockBase + (device->SamplesDone * DEVICE_CLOCK_RES /
|
||||
device->Frequency);
|
||||
}
|
||||
|
||||
|
||||
struct ALCbackendVtable;
|
||||
|
||||
typedef struct ALCbackend {
|
||||
const struct ALCbackendVtable *vtbl;
|
||||
|
||||
ALCdevice *mDevice;
|
||||
|
||||
almtx_t mMutex;
|
||||
} ALCbackend;
|
||||
|
||||
void ALCbackend_Construct(ALCbackend *self, ALCdevice *device);
|
||||
void ALCbackend_Destruct(ALCbackend *self);
|
||||
ALCboolean ALCbackend_reset(ALCbackend *self);
|
||||
ALCenum ALCbackend_captureSamples(ALCbackend *self, void *buffer, ALCuint samples);
|
||||
ALCuint ALCbackend_availableSamples(ALCbackend *self);
|
||||
ClockLatency ALCbackend_getClockLatency(ALCbackend *self);
|
||||
void ALCbackend_lock(ALCbackend *self);
|
||||
void ALCbackend_unlock(ALCbackend *self);
|
||||
|
||||
struct ALCbackendVtable {
|
||||
void (*const Destruct)(ALCbackend*);
|
||||
|
||||
ALCenum (*const open)(ALCbackend*, const ALCchar*);
|
||||
|
||||
ALCboolean (*const reset)(ALCbackend*);
|
||||
ALCboolean (*const start)(ALCbackend*);
|
||||
void (*const stop)(ALCbackend*);
|
||||
|
||||
ALCenum (*const captureSamples)(ALCbackend*, void*, ALCuint);
|
||||
ALCuint (*const availableSamples)(ALCbackend*);
|
||||
|
||||
ClockLatency (*const getClockLatency)(ALCbackend*);
|
||||
|
||||
void (*const lock)(ALCbackend*);
|
||||
void (*const unlock)(ALCbackend*);
|
||||
|
||||
void (*const Delete)(void*);
|
||||
};
|
||||
|
||||
#define DEFINE_ALCBACKEND_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, Destruct) \
|
||||
DECLARE_THUNK1(T, ALCbackend, ALCenum, open, const ALCchar*) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCboolean, reset) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCboolean, start) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, stop) \
|
||||
DECLARE_THUNK2(T, ALCbackend, ALCenum, captureSamples, void*, ALCuint) \
|
||||
DECLARE_THUNK(T, ALCbackend, ALCuint, availableSamples) \
|
||||
DECLARE_THUNK(T, ALCbackend, ClockLatency, getClockLatency) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, lock) \
|
||||
DECLARE_THUNK(T, ALCbackend, void, unlock) \
|
||||
static void T##_ALCbackend_Delete(void *ptr) \
|
||||
{ T##_Delete(STATIC_UPCAST(T, ALCbackend, (ALCbackend*)ptr)); } \
|
||||
\
|
||||
static const struct ALCbackendVtable T##_ALCbackend_vtable = { \
|
||||
T##_ALCbackend_Destruct, \
|
||||
\
|
||||
T##_ALCbackend_open, \
|
||||
T##_ALCbackend_reset, \
|
||||
T##_ALCbackend_start, \
|
||||
T##_ALCbackend_stop, \
|
||||
T##_ALCbackend_captureSamples, \
|
||||
T##_ALCbackend_availableSamples, \
|
||||
T##_ALCbackend_getClockLatency, \
|
||||
T##_ALCbackend_lock, \
|
||||
T##_ALCbackend_unlock, \
|
||||
\
|
||||
T##_ALCbackend_Delete, \
|
||||
}
|
||||
|
||||
|
||||
typedef enum ALCbackend_Type {
|
||||
ALCbackend_Playback,
|
||||
ALCbackend_Capture,
|
||||
ALCbackend_Loopback
|
||||
} ALCbackend_Type;
|
||||
|
||||
|
||||
struct ALCbackendFactoryVtable;
|
||||
|
||||
typedef struct ALCbackendFactory {
|
||||
const struct ALCbackendFactoryVtable *vtbl;
|
||||
} ALCbackendFactory;
|
||||
|
||||
void ALCbackendFactory_deinit(ALCbackendFactory *self);
|
||||
|
||||
struct ALCbackendFactoryVtable {
|
||||
ALCboolean (*const init)(ALCbackendFactory *self);
|
||||
void (*const deinit)(ALCbackendFactory *self);
|
||||
|
||||
ALCboolean (*const querySupport)(ALCbackendFactory *self, ALCbackend_Type type);
|
||||
|
||||
void (*const probe)(ALCbackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
|
||||
ALCbackend* (*const createBackend)(ALCbackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
};
|
||||
|
||||
#define DEFINE_ALCBACKENDFACTORY_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALCbackendFactory, ALCboolean, init) \
|
||||
DECLARE_THUNK(T, ALCbackendFactory, void, deinit) \
|
||||
DECLARE_THUNK1(T, ALCbackendFactory, ALCboolean, querySupport, ALCbackend_Type) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, void, probe, enum DevProbe, al_string*) \
|
||||
DECLARE_THUNK2(T, ALCbackendFactory, ALCbackend*, createBackend, ALCdevice*, ALCbackend_Type) \
|
||||
\
|
||||
static const struct ALCbackendFactoryVtable T##_ALCbackendFactory_vtable = { \
|
||||
T##_ALCbackendFactory_init, \
|
||||
T##_ALCbackendFactory_deinit, \
|
||||
T##_ALCbackendFactory_querySupport, \
|
||||
T##_ALCbackendFactory_probe, \
|
||||
T##_ALCbackendFactory_createBackend, \
|
||||
}
|
||||
|
||||
|
||||
ALCbackendFactory *ALCpulseBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCalsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCossBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCqsaBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwasapiBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCopenslBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
|
||||
|
||||
inline void ALCdevice_Lock(ALCdevice *device)
|
||||
{ V0(device->Backend,lock)(); }
|
||||
|
||||
inline void ALCdevice_Unlock(ALCdevice *device)
|
||||
{ V0(device->Backend,unlock)(); }
|
||||
|
||||
|
||||
inline ClockLatency GetClockLatency(ALCdevice *device)
|
||||
{
|
||||
ClockLatency ret = V0(device->Backend,getClockLatency)();
|
||||
ret.Latency += device->FixedLatency;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AL_BACKENDS_BASE_H */
|
||||
+371
-256
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -25,193 +25,143 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
typedef struct {
|
||||
AudioUnit audioUnit;
|
||||
|
||||
ALuint frameSize;
|
||||
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
|
||||
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
|
||||
|
||||
AudioConverterRef audioConverter; // Sample rate converter if needed
|
||||
AudioBufferList *bufferList; // Buffer for data coming from the input device
|
||||
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
|
||||
|
||||
RingBuffer *ring;
|
||||
} ca_data;
|
||||
|
||||
static const ALCchar ca_device[] = "CoreAudio Default";
|
||||
|
||||
|
||||
static void destroy_buffer_list(AudioBufferList* list)
|
||||
typedef struct ALCcoreAudioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
AudioUnit audioUnit;
|
||||
|
||||
ALuint frameSize;
|
||||
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
|
||||
} ALCcoreAudioPlayback;
|
||||
|
||||
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device);
|
||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self);
|
||||
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name);
|
||||
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self);
|
||||
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self);
|
||||
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCcoreAudioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcoreAudioPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioPlayback);
|
||||
|
||||
|
||||
static void ALCcoreAudioPlayback_Construct(ALCcoreAudioPlayback *self, ALCdevice *device)
|
||||
{
|
||||
if(list)
|
||||
{
|
||||
UInt32 i;
|
||||
for(i = 0;i < list->mNumberBuffers;i++)
|
||||
free(list->mBuffers[i].mData);
|
||||
free(list);
|
||||
}
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCcoreAudioPlayback, ALCbackend, self);
|
||||
|
||||
self->frameSize = 0;
|
||||
memset(&self->format, 0, sizeof(self->format));
|
||||
}
|
||||
|
||||
static AudioBufferList* allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
|
||||
static void ALCcoreAudioPlayback_Destruct(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
AudioBufferList *list;
|
||||
AudioUnitUninitialize(self->audioUnit);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
|
||||
list = calloc(1, sizeof(AudioBufferList) + sizeof(AudioBuffer));
|
||||
if(list)
|
||||
{
|
||||
list->mNumberBuffers = 1;
|
||||
|
||||
list->mBuffers[0].mNumberChannels = channelCount;
|
||||
list->mBuffers[0].mDataByteSize = byteSize;
|
||||
list->mBuffers[0].mData = malloc(byteSize);
|
||||
if(list->mBuffers[0].mData == NULL)
|
||||
{
|
||||
free(list);
|
||||
list = NULL;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
static OSStatus ca_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
static OSStatus ALCcoreAudioPlayback_MixerProc(void *inRefCon,
|
||||
AudioUnitRenderActionFlags* UNUSED(ioActionFlags), const AudioTimeStamp* UNUSED(inTimeStamp),
|
||||
UInt32 UNUSED(inBusNumber), UInt32 UNUSED(inNumberFrames), AudioBufferList *ioData)
|
||||
{
|
||||
ALCcoreAudioPlayback *self = inRefCon;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
ALCcoreAudioPlayback_lock(self);
|
||||
aluMixData(device, ioData->mBuffers[0].mData,
|
||||
ioData->mBuffers[0].mDataByteSize / data->frameSize);
|
||||
ioData->mBuffers[0].mDataByteSize / self->frameSize);
|
||||
ALCcoreAudioPlayback_unlock(self);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_conversion_callback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData)
|
||||
|
||||
static ALCenum ALCcoreAudioPlayback_open(ALCcoreAudioPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inUserData;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
// Read from the ring buffer and store temporarily in a large buffer
|
||||
ReadRingBuffer(data->ring, data->resampleBuffer, (ALsizei)(*ioNumberDataPackets));
|
||||
|
||||
// Set the input data
|
||||
ioData->mNumberBuffers = 1;
|
||||
ioData->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
ioData->mBuffers[0].mData = data->resampleBuffer;
|
||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * data->format.mBytesPerFrame;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ca_capture_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)inRefCon;
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
AudioComponentDescription desc;
|
||||
AudioComponent comp;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(data->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, data->bufferList);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender error: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
WriteRingBuffer(data->ring, data->bufferList->mBuffers[0].mData, inNumberFrames);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static ALCenum ca_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
ComponentDescription desc;
|
||||
Component comp;
|
||||
ca_data *data;
|
||||
OSStatus err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = ca_device;
|
||||
else if(strcmp(deviceName, ca_device) != 0)
|
||||
if(!name)
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
/* open the default output unit */
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
comp = AudioComponentFindNext(NULL, &desc);
|
||||
if(comp == NULL)
|
||||
{
|
||||
ERR("FindNextComponent failed\n");
|
||||
ERR("AudioComponentFindNext failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
|
||||
err = OpenAComponent(comp, &data->audioUnit);
|
||||
err = AudioComponentInstanceNew(comp, &self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("OpenAComponent failed\n");
|
||||
free(data);
|
||||
ERR("AudioComponentInstanceNew failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
/* init and start the default audio unit... */
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
err = AudioUnitInitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
CloseComponent(data->audioUnit);
|
||||
free(data);
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void ca_close_playback(ALCdevice *device)
|
||||
static ALCboolean ALCcoreAudioPlayback_reset(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
AudioUnitUninitialize(data->audioUnit);
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
AURenderCallbackStruct input;
|
||||
OSStatus err;
|
||||
UInt32 size;
|
||||
|
||||
err = AudioUnitUninitialize(data->audioUnit);
|
||||
err = AudioUnitUninitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("-- AudioUnitUninitialize failed.\n");
|
||||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
|
||||
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(AudioStreamBasicDescription))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
@@ -229,7 +179,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
#endif
|
||||
|
||||
/* set default output unit's input side to match output side */
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, size);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -238,7 +188,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
|
||||
if(device->Frequency != streamFormat.mSampleRate)
|
||||
{
|
||||
device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
|
||||
device->NumUpdates = (ALuint)((ALuint64)device->NumUpdates *
|
||||
streamFormat.mSampleRate /
|
||||
device->Frequency);
|
||||
device->Frequency = streamFormat.mSampleRate;
|
||||
@@ -277,40 +227,43 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
/* use channel count and sample rate from the default output unit's current
|
||||
* parameters, but reset everything else */
|
||||
streamFormat.mFramesPerPacket = 1;
|
||||
streamFormat.mFormatFlags = 0;
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtUByte:
|
||||
device->FmtType = DevFmtByte;
|
||||
/* fall-through */
|
||||
case DevFmtByte:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 8;
|
||||
streamFormat.mBytesPerPacket = streamFormat.mChannelsPerFrame;
|
||||
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
case DevFmtFloat:
|
||||
device->FmtType = DevFmtShort;
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 16;
|
||||
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame;
|
||||
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
device->FmtType = DevFmtInt;
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
streamFormat.mBitsPerChannel = 32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
||||
streamFormat.mBitsPerChannel = 32;
|
||||
streamFormat.mBytesPerPacket = 2 * streamFormat.mChannelsPerFrame;
|
||||
streamFormat.mBytesPerFrame = 2 * streamFormat.mChannelsPerFrame;
|
||||
break;
|
||||
}
|
||||
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame *
|
||||
streamFormat.mBitsPerChannel / 8;
|
||||
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame;
|
||||
streamFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
|
||||
kAudioFormatFlagsNativeEndian |
|
||||
kLinearPCMFormatFlagIsPacked;
|
||||
streamFormat.mFormatFlags |= kAudioFormatFlagsNativeEndian |
|
||||
kLinearPCMFormatFlagIsPacked;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, sizeof(AudioStreamBasicDescription));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -318,11 +271,11 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
}
|
||||
|
||||
/* setup callback */
|
||||
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
input.inputProc = ca_callback;
|
||||
input.inputProcRefCon = device;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
input.inputProc = ALCcoreAudioPlayback_MixerProc;
|
||||
input.inputProcRefCon = self;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -330,7 +283,7 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
}
|
||||
|
||||
/* init the default audio unit... */
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
err = AudioUnitInitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
@@ -340,12 +293,9 @@ static ALCboolean ca_reset_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ca_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCcoreAudioPlayback_start(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStart(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStart(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioOutputUnitStart failed\n");
|
||||
@@ -355,59 +305,196 @@ static ALCboolean ca_start_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ca_stop_playback(ALCdevice *device)
|
||||
static void ALCcoreAudioPlayback_stop(ALCcoreAudioPlayback *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err;
|
||||
|
||||
err = AudioOutputUnitStop(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStop(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
|
||||
|
||||
typedef struct ALCcoreAudioCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
AudioUnit audioUnit;
|
||||
|
||||
ALuint frameSize;
|
||||
ALdouble sampleRateRatio; // Ratio of hardware sample rate / requested sample rate
|
||||
AudioStreamBasicDescription format; // This is the OpenAL format as a CoreAudio ASBD
|
||||
|
||||
AudioConverterRef audioConverter; // Sample rate converter if needed
|
||||
AudioBufferList *bufferList; // Buffer for data coming from the input device
|
||||
ALCvoid *resampleBuffer; // Buffer for returned RingBuffer data when resampling
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
} ALCcoreAudioCapture;
|
||||
|
||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device);
|
||||
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self);
|
||||
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name);
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self);
|
||||
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self);
|
||||
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCcoreAudioCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCcoreAudioCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCcoreAudioCapture);
|
||||
|
||||
|
||||
static AudioBufferList *allocate_buffer_list(UInt32 channelCount, UInt32 byteSize)
|
||||
{
|
||||
AudioBufferList *list;
|
||||
|
||||
list = calloc(1, FAM_SIZE(AudioBufferList, mBuffers, 1) + byteSize);
|
||||
if(list)
|
||||
{
|
||||
list->mNumberBuffers = 1;
|
||||
|
||||
list->mBuffers[0].mNumberChannels = channelCount;
|
||||
list->mBuffers[0].mDataByteSize = byteSize;
|
||||
list->mBuffers[0].mData = &list->mBuffers[1];
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static void destroy_buffer_list(AudioBufferList *list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
|
||||
static void ALCcoreAudioCapture_Construct(ALCcoreAudioCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCcoreAudioCapture, ALCbackend, self);
|
||||
|
||||
self->audioUnit = 0;
|
||||
self->audioConverter = NULL;
|
||||
self->bufferList = NULL;
|
||||
self->resampleBuffer = NULL;
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
static void ALCcoreAudioCapture_Destruct(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
free(self->resampleBuffer);
|
||||
self->resampleBuffer = NULL;
|
||||
|
||||
destroy_buffer_list(self->bufferList);
|
||||
self->bufferList = NULL;
|
||||
|
||||
if(self->audioConverter)
|
||||
AudioConverterDispose(self->audioConverter);
|
||||
self->audioConverter = NULL;
|
||||
|
||||
if(self->audioUnit)
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
self->audioUnit = 0;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static OSStatus ALCcoreAudioCapture_RecordProc(void *inRefCon,
|
||||
AudioUnitRenderActionFlags* UNUSED(ioActionFlags),
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 UNUSED(inBusNumber),
|
||||
UInt32 inNumberFrames, AudioBufferList* UNUSED(ioData))
|
||||
{
|
||||
ALCcoreAudioCapture *self = inRefCon;
|
||||
AudioUnitRenderActionFlags flags = 0;
|
||||
OSStatus err;
|
||||
|
||||
// fill the bufferList with data from the input device
|
||||
err = AudioUnitRender(self->audioUnit, &flags, inTimeStamp, 1, inNumberFrames, self->bufferList);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender error: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ll_ringbuffer_write(self->ring, self->bufferList->mBuffers[0].mData, inNumberFrames);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus ALCcoreAudioCapture_ConvertCallback(AudioConverterRef UNUSED(inAudioConverter),
|
||||
UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription** UNUSED(outDataPacketDescription),
|
||||
void *inUserData)
|
||||
{
|
||||
ALCcoreAudioCapture *self = inUserData;
|
||||
|
||||
// Read from the ring buffer and store temporarily in a large buffer
|
||||
ll_ringbuffer_read(self->ring, self->resampleBuffer, *ioNumberDataPackets);
|
||||
|
||||
// Set the input data
|
||||
ioData->mNumberBuffers = 1;
|
||||
ioData->mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
|
||||
ioData->mBuffers[0].mData = self->resampleBuffer;
|
||||
ioData->mBuffers[0].mDataByteSize = (*ioNumberDataPackets) * self->format.mBytesPerFrame;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCcoreAudioCapture_open(ALCcoreAudioCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
AudioStreamBasicDescription requestedFormat; // The application requested format
|
||||
AudioStreamBasicDescription hardwareFormat; // The hardware format
|
||||
AudioStreamBasicDescription outputFormat; // The AudioUnit output format
|
||||
AURenderCallbackStruct input;
|
||||
ComponentDescription desc;
|
||||
AudioDeviceID inputDevice;
|
||||
AudioComponentDescription desc;
|
||||
UInt32 outputFrameCount;
|
||||
UInt32 propertySize;
|
||||
AudioObjectPropertyAddress propertyAddress;
|
||||
UInt32 enableIO;
|
||||
Component comp;
|
||||
ca_data *data;
|
||||
AudioComponent comp;
|
||||
OSStatus err;
|
||||
|
||||
if(!name)
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
// Search for component with given description
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
comp = AudioComponentFindNext(NULL, &desc);
|
||||
if(comp == NULL)
|
||||
{
|
||||
ERR("FindNextComponent failed\n");
|
||||
ERR("AudioComponentFindNext failed\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
device->ExtraData = data;
|
||||
|
||||
// Open the component
|
||||
err = OpenAComponent(comp, &data->audioUnit);
|
||||
err = AudioComponentInstanceNew(comp, &self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("OpenAComponent failed\n");
|
||||
ERR("AudioComponentInstanceNew failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Turn off AudioUnit output
|
||||
enableIO = 0;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(ALuint));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -416,22 +503,28 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
// Turn on AudioUnit input
|
||||
enableIO = 1;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(ALuint));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_IOS
|
||||
// Get the default input device
|
||||
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
||||
|
||||
propertySize = sizeof(AudioDeviceID);
|
||||
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propertySize, &inputDevice);
|
||||
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
|
||||
|
||||
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &inputDevice);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioHardwareGetProperty failed\n");
|
||||
ERR("AudioObjectGetPropertyData failed\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(inputDevice == kAudioDeviceUnknown)
|
||||
{
|
||||
ERR("No input device found\n");
|
||||
@@ -439,18 +532,19 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Track the input device
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDevice, sizeof(AudioDeviceID));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
// set capture callback
|
||||
input.inputProc = ca_capture_callback;
|
||||
input.inputProcRefCon = device;
|
||||
input.inputProc = ALCcoreAudioCapture_RecordProc;
|
||||
input.inputProcRefCon = self;
|
||||
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -458,7 +552,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Initialize the device
|
||||
err = AudioUnitInitialize(data->audioUnit);
|
||||
err = AudioUnitInitialize(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
@@ -467,7 +561,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
// Get the hardware format
|
||||
propertySize = sizeof(AudioStreamBasicDescription);
|
||||
err = AudioUnitGetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
||||
err = AudioUnitGetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(AudioStreamBasicDescription))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
@@ -511,9 +605,10 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
case DevFmtQuad:
|
||||
case DevFmtX51:
|
||||
case DevFmtX51Side:
|
||||
case DevFmtX51Rear:
|
||||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtAmbi3D:
|
||||
ERR("%s not supported\n", DevFmtChannelsString(device->FmtChans));
|
||||
goto error;
|
||||
}
|
||||
@@ -526,8 +621,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
requestedFormat.mFramesPerPacket = 1;
|
||||
|
||||
// save requested format description for later use
|
||||
data->format = requestedFormat;
|
||||
data->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
self->format = requestedFormat;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
// Use intermediate format for sample rate conversion (outputFormat)
|
||||
// Set sample rate to the same as hardware for resampling later
|
||||
@@ -535,11 +630,11 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
outputFormat.mSampleRate = hardwareFormat.mSampleRate;
|
||||
|
||||
// Determine sample rate ratio for resampling
|
||||
data->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
|
||||
self->sampleRateRatio = outputFormat.mSampleRate / device->Frequency;
|
||||
|
||||
// The output format should be the requested format, but using the hardware sample rate
|
||||
// This is because the AudioUnit will automatically scale other properties, except for sample rate
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, (void *)&outputFormat, sizeof(outputFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
@@ -547,8 +642,8 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Set the AudioUnit output format frame count
|
||||
outputFrameCount = device->UpdateSize * data->sampleRateRatio;
|
||||
err = AudioUnitSetProperty(data->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
||||
outputFrameCount = device->UpdateSize * self->sampleRateRatio;
|
||||
err = AudioUnitSetProperty(self->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, 0, &outputFrameCount, sizeof(outputFrameCount));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed: %d\n", err);
|
||||
@@ -556,7 +651,7 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Set up sample converter
|
||||
err = AudioConverterNew(&outputFormat, &requestedFormat, &data->audioConverter);
|
||||
err = AudioConverterNew(&outputFormat, &requestedFormat, &self->audioConverter);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioConverterNew failed: %d\n", err);
|
||||
@@ -564,90 +659,83 @@ static ALCenum ca_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
}
|
||||
|
||||
// Create a buffer for use in the resample callback
|
||||
data->resampleBuffer = malloc(device->UpdateSize * data->frameSize * data->sampleRateRatio);
|
||||
self->resampleBuffer = malloc(device->UpdateSize * self->frameSize * self->sampleRateRatio);
|
||||
|
||||
// Allocate buffer for the AudioUnit output
|
||||
data->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * data->frameSize * data->sampleRateRatio);
|
||||
if(data->bufferList == NULL)
|
||||
self->bufferList = allocate_buffer_list(outputFormat.mChannelsPerFrame, device->UpdateSize * self->frameSize * self->sampleRateRatio);
|
||||
if(self->bufferList == NULL)
|
||||
goto error;
|
||||
|
||||
data->ring = CreateRingBuffer(data->frameSize, (device->UpdateSize * data->sampleRateRatio) * device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
self->ring = ll_ringbuffer_create(
|
||||
(size_t)ceil(device->UpdateSize*self->sampleRateRatio*device->NumUpdates),
|
||||
self->frameSize, false
|
||||
);
|
||||
if(!self->ring) goto error;
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data->resampleBuffer);
|
||||
destroy_buffer_list(data->bufferList);
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
free(self->resampleBuffer);
|
||||
self->resampleBuffer = NULL;
|
||||
destroy_buffer_list(self->bufferList);
|
||||
self->bufferList = NULL;
|
||||
|
||||
if(data->audioConverter)
|
||||
AudioConverterDispose(data->audioConverter);
|
||||
if(data->audioUnit)
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
if(self->audioConverter)
|
||||
AudioConverterDispose(self->audioConverter);
|
||||
self->audioConverter = NULL;
|
||||
if(self->audioUnit)
|
||||
AudioComponentInstanceDispose(self->audioUnit);
|
||||
self->audioUnit = 0;
|
||||
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void ca_close_capture(ALCdevice *device)
|
||||
|
||||
static ALCboolean ALCcoreAudioCapture_start(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data->resampleBuffer);
|
||||
destroy_buffer_list(data->bufferList);
|
||||
|
||||
AudioConverterDispose(data->audioConverter);
|
||||
CloseComponent(data->audioUnit);
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void ca_start_capture(ALCdevice *device)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err = AudioOutputUnitStart(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStart(self->audioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioOutputUnitStart failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ca_stop_capture(ALCdevice *device)
|
||||
static void ALCcoreAudioCapture_stop(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
OSStatus err = AudioOutputUnitStop(data->audioUnit);
|
||||
OSStatus err = AudioOutputUnitStop(self->audioUnit);
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
}
|
||||
|
||||
static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
static ALCenum ALCcoreAudioCapture_captureSamples(ALCcoreAudioCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ca_data *data = (ca_data*)device->ExtraData;
|
||||
AudioBufferList *list;
|
||||
union {
|
||||
ALbyte _[sizeof(AudioBufferList) + sizeof(AudioBuffer)];
|
||||
AudioBufferList list;
|
||||
} audiobuf = { { 0 } };
|
||||
UInt32 frameCount;
|
||||
OSStatus err;
|
||||
|
||||
// If no samples are requested, just return
|
||||
if(samples == 0)
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
// Allocate a temporary AudioBufferList to use as the return resamples data
|
||||
list = alloca(sizeof(AudioBufferList) + sizeof(AudioBuffer));
|
||||
if(samples == 0) return ALC_NO_ERROR;
|
||||
|
||||
// Point the resampling buffer to the capture buffer
|
||||
list->mNumberBuffers = 1;
|
||||
list->mBuffers[0].mNumberChannels = data->format.mChannelsPerFrame;
|
||||
list->mBuffers[0].mDataByteSize = samples * data->frameSize;
|
||||
list->mBuffers[0].mData = buffer;
|
||||
audiobuf.list.mNumberBuffers = 1;
|
||||
audiobuf.list.mBuffers[0].mNumberChannels = self->format.mChannelsPerFrame;
|
||||
audiobuf.list.mBuffers[0].mDataByteSize = samples * self->frameSize;
|
||||
audiobuf.list.mBuffers[0].mData = buffer;
|
||||
|
||||
// Resample into another AudioBufferList
|
||||
frameCount = samples;
|
||||
err = AudioConverterFillComplexBuffer(data->audioConverter, ca_capture_conversion_callback,
|
||||
device, &frameCount, list, NULL);
|
||||
err = AudioConverterFillComplexBuffer(self->audioConverter,
|
||||
ALCcoreAudioCapture_ConvertCallback, self, &frameCount, &audiobuf.list, NULL
|
||||
);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioConverterFillComplexBuffer error: %d\n", err);
|
||||
@@ -656,46 +744,73 @@ static ALCenum ca_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint sa
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint ca_available_samples(ALCdevice *device)
|
||||
static ALCuint ALCcoreAudioCapture_availableSamples(ALCcoreAudioCapture *self)
|
||||
{
|
||||
ca_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring) / data->sampleRateRatio;
|
||||
return ll_ringbuffer_read_space(self->ring) / self->sampleRateRatio;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs ca_funcs = {
|
||||
ca_open_playback,
|
||||
ca_close_playback,
|
||||
ca_reset_playback,
|
||||
ca_start_playback,
|
||||
ca_stop_playback,
|
||||
ca_open_capture,
|
||||
ca_close_capture,
|
||||
ca_start_capture,
|
||||
ca_stop_capture,
|
||||
ca_capture_samples,
|
||||
ca_available_samples
|
||||
};
|
||||
typedef struct ALCcoreAudioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCcoreAudioBackendFactory;
|
||||
#define ALCCOREAUDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCcoreAudioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_ca_init(BackendFuncs *func_list)
|
||||
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCcoreAudioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCcoreAudioBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCcoreAudioBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCcoreAudioBackendFactory factory = ALCCOREAUDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCcoreAudioBackendFactory_init(ALCcoreAudioBackendFactory* UNUSED(self))
|
||||
{
|
||||
*func_list = ca_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_ca_deinit(void)
|
||||
static ALCboolean ALCcoreAudioBackendFactory_querySupport(ALCcoreAudioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_ca_probe(enum DevProbe type)
|
||||
static void ALCcoreAudioBackendFactory_probe(ALCcoreAudioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDeviceList(ca_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(ca_device);
|
||||
alstr_append_range(outnames, ca_device, ca_device+sizeof(ca_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCcoreAudioBackendFactory_createBackend(ALCcoreAudioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCcoreAudioPlayback *backend;
|
||||
NEW_OBJ(backend, ALCcoreAudioPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCcoreAudioCapture *backend;
|
||||
NEW_OBJ(backend, ALCcoreAudioCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+550
-495
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,607 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
|
||||
|
||||
static const ALCchar jackDevice[] = "JACK Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define JACK_FUNCS(MAGIC) \
|
||||
MAGIC(jack_client_open); \
|
||||
MAGIC(jack_client_close); \
|
||||
MAGIC(jack_client_name_size); \
|
||||
MAGIC(jack_get_client_name); \
|
||||
MAGIC(jack_connect); \
|
||||
MAGIC(jack_activate); \
|
||||
MAGIC(jack_deactivate); \
|
||||
MAGIC(jack_port_register); \
|
||||
MAGIC(jack_port_unregister); \
|
||||
MAGIC(jack_port_get_buffer); \
|
||||
MAGIC(jack_port_name); \
|
||||
MAGIC(jack_get_ports); \
|
||||
MAGIC(jack_free); \
|
||||
MAGIC(jack_get_sample_rate); \
|
||||
MAGIC(jack_set_error_function); \
|
||||
MAGIC(jack_set_process_callback); \
|
||||
MAGIC(jack_set_buffer_size_callback); \
|
||||
MAGIC(jack_set_buffer_size); \
|
||||
MAGIC(jack_get_buffer_size);
|
||||
|
||||
static void *jack_handle;
|
||||
#define MAKE_FUNC(f) static __typeof(f) * p##f
|
||||
JACK_FUNCS(MAKE_FUNC);
|
||||
static __typeof(jack_error_callback) * pjack_error_callback;
|
||||
#undef MAKE_FUNC
|
||||
|
||||
#define jack_client_open pjack_client_open
|
||||
#define jack_client_close pjack_client_close
|
||||
#define jack_client_name_size pjack_client_name_size
|
||||
#define jack_get_client_name pjack_get_client_name
|
||||
#define jack_connect pjack_connect
|
||||
#define jack_activate pjack_activate
|
||||
#define jack_deactivate pjack_deactivate
|
||||
#define jack_port_register pjack_port_register
|
||||
#define jack_port_unregister pjack_port_unregister
|
||||
#define jack_port_get_buffer pjack_port_get_buffer
|
||||
#define jack_port_name pjack_port_name
|
||||
#define jack_get_ports pjack_get_ports
|
||||
#define jack_free pjack_free
|
||||
#define jack_get_sample_rate pjack_get_sample_rate
|
||||
#define jack_set_error_function pjack_set_error_function
|
||||
#define jack_set_process_callback pjack_set_process_callback
|
||||
#define jack_set_buffer_size_callback pjack_set_buffer_size_callback
|
||||
#define jack_set_buffer_size pjack_set_buffer_size
|
||||
#define jack_get_buffer_size pjack_get_buffer_size
|
||||
#define jack_error_callback (*pjack_error_callback)
|
||||
#endif
|
||||
|
||||
|
||||
static jack_options_t ClientOptions = JackNullOption;
|
||||
|
||||
static ALCboolean jack_load(void)
|
||||
{
|
||||
ALCboolean error = ALC_FALSE;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!jack_handle)
|
||||
{
|
||||
al_string missing_funcs = AL_STRING_INIT_STATIC();
|
||||
|
||||
#ifdef _WIN32
|
||||
#define JACKLIB "libjack.dll"
|
||||
#else
|
||||
#define JACKLIB "libjack.so.0"
|
||||
#endif
|
||||
jack_handle = LoadLib(JACKLIB);
|
||||
if(!jack_handle)
|
||||
{
|
||||
WARN("Failed to load %s\n", JACKLIB);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
error = ALC_FALSE;
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(jack_handle, #f); \
|
||||
if(p##f == NULL) { \
|
||||
error = ALC_TRUE; \
|
||||
alstr_append_cstr(&missing_funcs, "\n" #f); \
|
||||
} \
|
||||
} while(0)
|
||||
JACK_FUNCS(LOAD_FUNC);
|
||||
#undef LOAD_FUNC
|
||||
/* Optional symbols. These don't exist in all versions of JACK. */
|
||||
#define LOAD_SYM(f) p##f = GetSymbol(jack_handle, #f)
|
||||
LOAD_SYM(jack_error_callback);
|
||||
#undef LOAD_SYM
|
||||
|
||||
if(error)
|
||||
{
|
||||
WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs));
|
||||
CloseLib(jack_handle);
|
||||
jack_handle = NULL;
|
||||
}
|
||||
alstr_reset(&missing_funcs);
|
||||
}
|
||||
#endif
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCjackPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
jack_client_t *Client;
|
||||
jack_port_t *Port[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
ll_ringbuffer_t *Ring;
|
||||
alsem_t Sem;
|
||||
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCjackPlayback;
|
||||
|
||||
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg);
|
||||
|
||||
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg);
|
||||
static int ALCjackPlayback_mixerProc(void *arg);
|
||||
|
||||
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device);
|
||||
static void ALCjackPlayback_Destruct(ALCjackPlayback *self);
|
||||
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name);
|
||||
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self);
|
||||
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self);
|
||||
static void ALCjackPlayback_stop(ALCjackPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCjackPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self);
|
||||
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCjackPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCjackPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCjackPlayback);
|
||||
|
||||
|
||||
static void ALCjackPlayback_Construct(ALCjackPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCjackPlayback, ALCbackend, self);
|
||||
|
||||
alsem_init(&self->Sem, 0);
|
||||
|
||||
self->Client = NULL;
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
self->Port[i] = NULL;
|
||||
self->Ring = NULL;
|
||||
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_Destruct(ALCjackPlayback *self)
|
||||
{
|
||||
ALuint i;
|
||||
|
||||
if(self->Client)
|
||||
{
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
if(self->Port[i])
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
jack_client_close(self->Client);
|
||||
self->Client = NULL;
|
||||
}
|
||||
|
||||
alsem_destroy(&self->Sem);
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCjackPlayback_bufferSizeNotify(jack_nframes_t numframes, void *arg)
|
||||
{
|
||||
ALCjackPlayback *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
ALuint bufsize;
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
device->UpdateSize = numframes;
|
||||
device->NumUpdates = 2;
|
||||
|
||||
bufsize = device->UpdateSize;
|
||||
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
|
||||
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
|
||||
device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
|
||||
|
||||
TRACE("%u update size x%u\n", device->UpdateSize, device->NumUpdates);
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(bufsize,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
|
||||
true
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
ERR("Failed to reallocate ringbuffer\n");
|
||||
aluHandleDisconnect(device, "Failed to reallocate %u-sample buffer", bufsize);
|
||||
}
|
||||
ALCjackPlayback_unlock(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ALCjackPlayback_process(jack_nframes_t numframes, void *arg)
|
||||
{
|
||||
ALCjackPlayback *self = arg;
|
||||
jack_default_audio_sample_t *out[MAX_OUTPUT_CHANNELS];
|
||||
ll_ringbuffer_data_t data[2];
|
||||
jack_nframes_t total = 0;
|
||||
jack_nframes_t todo;
|
||||
ALsizei i, c, numchans;
|
||||
|
||||
ll_ringbuffer_get_read_vector(self->Ring, data);
|
||||
|
||||
for(c = 0;c < MAX_OUTPUT_CHANNELS && self->Port[c];c++)
|
||||
out[c] = jack_port_get_buffer(self->Port[c], numframes);
|
||||
numchans = c;
|
||||
|
||||
todo = minu(numframes, data[0].len);
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
const ALfloat *restrict in = ((ALfloat*)data[0].buf) + c;
|
||||
for(i = 0;(jack_nframes_t)i < todo;i++)
|
||||
out[c][i] = in[i*numchans];
|
||||
out[c] += todo;
|
||||
}
|
||||
total += todo;
|
||||
|
||||
todo = minu(numframes-total, data[1].len);
|
||||
if(todo > 0)
|
||||
{
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
const ALfloat *restrict in = ((ALfloat*)data[1].buf) + c;
|
||||
for(i = 0;(jack_nframes_t)i < todo;i++)
|
||||
out[c][i] = in[i*numchans];
|
||||
out[c] += todo;
|
||||
}
|
||||
total += todo;
|
||||
}
|
||||
|
||||
ll_ringbuffer_read_advance(self->Ring, total);
|
||||
alsem_post(&self->Sem);
|
||||
|
||||
if(numframes > total)
|
||||
{
|
||||
todo = numframes-total;
|
||||
for(c = 0;c < numchans;c++)
|
||||
{
|
||||
for(i = 0;(jack_nframes_t)i < todo;i++)
|
||||
out[c][i] = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ALCjackPlayback_mixerProc(void *arg)
|
||||
{
|
||||
ALCjackPlayback *self = arg;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
ll_ringbuffer_data_t data[2];
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ALuint todo, len1, len2;
|
||||
|
||||
if(ll_ringbuffer_write_space(self->Ring) < device->UpdateSize)
|
||||
{
|
||||
ALCjackPlayback_unlock(self);
|
||||
alsem_wait(&self->Sem);
|
||||
ALCjackPlayback_lock(self);
|
||||
continue;
|
||||
}
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->Ring, data);
|
||||
todo = data[0].len + data[1].len;
|
||||
todo -= todo%device->UpdateSize;
|
||||
|
||||
len1 = minu(data[0].len, todo);
|
||||
len2 = minu(data[1].len, todo-len1);
|
||||
|
||||
aluMixData(device, data[0].buf, len1);
|
||||
if(len2 > 0)
|
||||
aluMixData(device, data[1].buf, len2);
|
||||
ll_ringbuffer_write_advance(self->Ring, todo);
|
||||
}
|
||||
ALCjackPlayback_unlock(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCjackPlayback_open(ALCjackPlayback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const char *client_name = "alsoft";
|
||||
jack_status_t status;
|
||||
|
||||
if(!name)
|
||||
name = jackDevice;
|
||||
else if(strcmp(name, jackDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->Client = jack_client_open(client_name, ClientOptions, &status, NULL);
|
||||
if(self->Client == NULL)
|
||||
{
|
||||
ERR("jack_client_open() failed, status = 0x%02x\n", status);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
if((status&JackServerStarted))
|
||||
TRACE("JACK server started\n");
|
||||
if((status&JackNameNotUnique))
|
||||
{
|
||||
client_name = jack_get_client_name(self->Client);
|
||||
TRACE("Client name not unique, got `%s' instead\n", client_name);
|
||||
}
|
||||
|
||||
jack_set_process_callback(self->Client, ALCjackPlayback_process, self);
|
||||
jack_set_buffer_size_callback(self->Client, ALCjackPlayback_bufferSizeNotify, self);
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackPlayback_reset(ALCjackPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei numchans, i;
|
||||
ALuint bufsize;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
{
|
||||
if(self->Port[i])
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
|
||||
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
|
||||
* ready for when requested.
|
||||
*/
|
||||
device->Frequency = jack_get_sample_rate(self->Client);
|
||||
device->UpdateSize = jack_get_buffer_size(self->Client);
|
||||
device->NumUpdates = 2;
|
||||
|
||||
bufsize = device->UpdateSize;
|
||||
if(ConfigValueUInt(alstr_get_cstr(device->DeviceName), "jack", "buffer-size", &bufsize))
|
||||
bufsize = maxu(NextPowerOf2(bufsize), device->UpdateSize);
|
||||
device->NumUpdates = (bufsize+device->UpdateSize) / device->UpdateSize;
|
||||
|
||||
/* Force 32-bit float output. */
|
||||
device->FmtType = DevFmtFloat;
|
||||
|
||||
numchans = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "channel_%d", i+1);
|
||||
self->Port[i] = jack_port_register(self->Client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||
if(self->Port[i] == NULL)
|
||||
{
|
||||
ERR("Not enough JACK ports available for %s output\n", DevFmtChannelsString(device->FmtChans));
|
||||
if(i == 0) return ALC_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i < numchans)
|
||||
{
|
||||
if(i == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
for(--i;i >= 2;i--)
|
||||
{
|
||||
jack_port_unregister(self->Client, self->Port[i]);
|
||||
self->Port[i] = NULL;
|
||||
}
|
||||
device->FmtChans = DevFmtStereo;
|
||||
}
|
||||
}
|
||||
|
||||
ll_ringbuffer_free(self->Ring);
|
||||
self->Ring = ll_ringbuffer_create(bufsize,
|
||||
FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder),
|
||||
true
|
||||
);
|
||||
if(!self->Ring)
|
||||
{
|
||||
ERR("Failed to allocate ringbuffer\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackPlayback_start(ALCjackPlayback *self)
|
||||
{
|
||||
const char **ports;
|
||||
ALsizei i;
|
||||
|
||||
if(jack_activate(self->Client))
|
||||
{
|
||||
ERR("Failed to activate client\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
ports = jack_get_ports(self->Client, NULL, NULL, JackPortIsPhysical|JackPortIsInput);
|
||||
if(ports == NULL)
|
||||
{
|
||||
ERR("No physical playback ports found\n");
|
||||
jack_deactivate(self->Client);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS && self->Port[i];i++)
|
||||
{
|
||||
if(!ports[i])
|
||||
{
|
||||
ERR("No physical playback port for \"%s\"\n", jack_port_name(self->Port[i]));
|
||||
break;
|
||||
}
|
||||
if(jack_connect(self->Client, jack_port_name(self->Port[i]), ports[i]))
|
||||
ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(self->Port[i]), ports[i]);
|
||||
}
|
||||
jack_free(ports);
|
||||
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCjackPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
jack_deactivate(self->Client);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCjackPlayback_stop(ALCjackPlayback *self)
|
||||
{
|
||||
int res;
|
||||
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
alsem_post(&self->Sem);
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
jack_deactivate(self->Client);
|
||||
}
|
||||
|
||||
|
||||
static ClockLatency ALCjackPlayback_getClockLatency(ALCjackPlayback *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ClockLatency ret;
|
||||
|
||||
ALCjackPlayback_lock(self);
|
||||
ret.ClockTime = GetDeviceClockTime(device);
|
||||
ret.Latency = ll_ringbuffer_read_space(self->Ring) * DEVICE_CLOCK_RES /
|
||||
device->Frequency;
|
||||
ALCjackPlayback_unlock(self);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void jack_msg_handler(const char *message)
|
||||
{
|
||||
WARN("%s\n", message);
|
||||
}
|
||||
|
||||
typedef struct ALCjackBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCjackBackendFactory;
|
||||
#define ALCJACKBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCjackBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCjackBackendFactory_init(ALCjackBackendFactory* UNUSED(self))
|
||||
{
|
||||
void (*old_error_cb)(const char*);
|
||||
jack_client_t *client;
|
||||
jack_status_t status;
|
||||
|
||||
if(!jack_load())
|
||||
return ALC_FALSE;
|
||||
|
||||
if(!GetConfigValueBool(NULL, "jack", "spawn-server", 0))
|
||||
ClientOptions |= JackNoStartServer;
|
||||
|
||||
old_error_cb = (&jack_error_callback ? jack_error_callback : NULL);
|
||||
jack_set_error_function(jack_msg_handler);
|
||||
client = jack_client_open("alsoft", ClientOptions, &status, NULL);
|
||||
jack_set_error_function(old_error_cb);
|
||||
if(client == NULL)
|
||||
{
|
||||
WARN("jack_client_open() failed, 0x%02x\n", status);
|
||||
if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer))
|
||||
ERR("Unable to connect to JACK server\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
jack_client_close(client);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCjackBackendFactory_deinit(ALCjackBackendFactory* UNUSED(self))
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(jack_handle)
|
||||
CloseLib(jack_handle);
|
||||
jack_handle = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ALCboolean ALCjackBackendFactory_querySupport(ALCjackBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCjackBackendFactory_probe(ALCjackBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, jackDevice, jackDevice+sizeof(jackDevice));
|
||||
break;
|
||||
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCjackBackendFactory_createBackend(ALCjackBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCjackPlayback *backend;
|
||||
NEW_OBJ(backend, ALCjackPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCjackBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCjackBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCjackBackendFactory factory = ALCJACKBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
+79
-35
@@ -13,72 +13,116 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
static ALCenum loopback_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
typedef struct ALCloopback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
} ALCloopback;
|
||||
|
||||
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name);
|
||||
static ALCboolean ALCloopback_reset(ALCloopback *self);
|
||||
static ALCboolean ALCloopback_start(ALCloopback *self);
|
||||
static void ALCloopback_stop(ALCloopback *self);
|
||||
static DECLARE_FORWARD2(ALCloopback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCloopback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCloopback)
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCloopback);
|
||||
|
||||
|
||||
static void ALCloopback_Construct(ALCloopback *self, ALCdevice *device)
|
||||
{
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCloopback, ALCbackend, self);
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCloopback_open(ALCloopback *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void loopback_close_playback(ALCdevice *device)
|
||||
static ALCboolean ALCloopback_reset(ALCloopback *self)
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
static ALCboolean loopback_reset_playback(ALCdevice *device)
|
||||
{
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean loopback_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCloopback_start(ALCloopback* UNUSED(self))
|
||||
{
|
||||
return ALC_TRUE;
|
||||
(void)device;
|
||||
}
|
||||
|
||||
static void loopback_stop_playback(ALCdevice *device)
|
||||
static void ALCloopback_stop(ALCloopback* UNUSED(self))
|
||||
{
|
||||
(void)device;
|
||||
}
|
||||
|
||||
static const BackendFuncs loopback_funcs = {
|
||||
loopback_open_playback,
|
||||
loopback_close_playback,
|
||||
loopback_reset_playback,
|
||||
loopback_start_playback,
|
||||
loopback_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
ALCboolean alc_loopback_init(BackendFuncs *func_list)
|
||||
typedef struct ALCloopbackFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCloopbackFactory;
|
||||
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCloopbackFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void);
|
||||
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory *self);
|
||||
static DECLARE_FORWARD(ALCloopbackFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory *self, ALCbackend_Type type);
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCloopbackFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCloopbackFactory_getFactory(void)
|
||||
{
|
||||
static ALCloopbackFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
static ALCboolean ALCloopbackFactory_init(ALCloopbackFactory* UNUSED(self))
|
||||
{
|
||||
*func_list = loopback_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_loopback_deinit(void)
|
||||
static ALCboolean ALCloopbackFactory_querySupport(ALCloopbackFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Loopback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCloopbackFactory_probe(ALCloopbackFactory* UNUSED(self), enum DevProbe UNUSED(type), al_string* UNUSED(outnames))
|
||||
{
|
||||
}
|
||||
|
||||
void alc_loopback_probe(enum DevProbe type)
|
||||
static ALCbackend* ALCloopbackFactory_createBackend(ALCloopbackFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
(void)type;
|
||||
if(type == ALCbackend_Loopback)
|
||||
{
|
||||
ALCloopback *backend;
|
||||
NEW_OBJ(backend, ALCloopback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+137
-83
@@ -13,154 +13,208 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_WINDOWS_H
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} null_data;
|
||||
typedef struct ALCnullBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} ALCnullBackend;
|
||||
|
||||
static int ALCnullBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device);
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, Destruct)
|
||||
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name);
|
||||
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self);
|
||||
static ALCboolean ALCnullBackend_start(ALCnullBackend *self);
|
||||
static void ALCnullBackend_stop(ALCnullBackend *self);
|
||||
static DECLARE_FORWARD2(ALCnullBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCnullBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCnullBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCnullBackend);
|
||||
|
||||
|
||||
static const ALCchar nullDevice[] = "No Output";
|
||||
|
||||
static ALuint NullProc(ALvoid *ptr)
|
||||
|
||||
static void ALCnullBackend_Construct(ALCnullBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *Device = (ALCdevice*)ptr;
|
||||
null_data *data = (null_data*)Device->ExtraData;
|
||||
ALuint now, start;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCnullBackend, ALCbackend, self);
|
||||
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
|
||||
static int ALCnullBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCnullBackend *self = (ALCnullBackend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct timespec now, start;
|
||||
ALuint64 avail, done;
|
||||
const ALuint restTime = (ALuint64)Device->UpdateSize * 1000 /
|
||||
Device->Frequency / 2;
|
||||
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
|
||||
device->Frequency / 2);
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
done = 0;
|
||||
start = timeGetTime();
|
||||
while(!data->killNow && Device->Connected)
|
||||
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
now = timeGetTime();
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get current time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
avail = (ALuint64)(now-start) * Device->Frequency / 1000;
|
||||
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
|
||||
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
|
||||
if(avail < done)
|
||||
{
|
||||
/* Timer wrapped (50 days???). Add the remainder of the cycle to
|
||||
* the available count and reset the number of samples done */
|
||||
avail += ((ALuint64)1<<32)*Device->Frequency/1000 - done;
|
||||
done = 0;
|
||||
}
|
||||
if(avail-done < Device->UpdateSize)
|
||||
{
|
||||
Sleep(restTime);
|
||||
continue;
|
||||
/* Oops, time skipped backwards. Reset the number of samples done
|
||||
* with one update available since we (likely) just came back from
|
||||
* sleeping. */
|
||||
done = avail - device->UpdateSize;
|
||||
}
|
||||
|
||||
while(avail-done >= Device->UpdateSize)
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(Device, NULL, Device->UpdateSize);
|
||||
done += Device->UpdateSize;
|
||||
ALCnullBackend_lock(self);
|
||||
aluMixData(device, NULL, device->UpdateSize);
|
||||
ALCnullBackend_unlock(self);
|
||||
done += device->UpdateSize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum null_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
{
|
||||
null_data *data;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = nullDevice;
|
||||
else if(strcmp(deviceName, nullDevice) != 0)
|
||||
static ALCenum ALCnullBackend_open(ALCnullBackend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device;
|
||||
|
||||
if(!name)
|
||||
name = nullDevice;
|
||||
else if(strcmp(name, nullDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (null_data*)calloc(1, sizeof(*data));
|
||||
device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void null_close_playback(ALCdevice *device)
|
||||
static ALCboolean ALCnullBackend_reset(ALCnullBackend *self)
|
||||
{
|
||||
null_data *data = (null_data*)device->ExtraData;
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean null_reset_playback(ALCdevice *device)
|
||||
{
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean null_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCnullBackend_start(ALCnullBackend *self)
|
||||
{
|
||||
null_data *data = (null_data*)device->ExtraData;
|
||||
|
||||
data->thread = StartThread(NullProc, device);
|
||||
if(data->thread == NULL)
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCnullBackend_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void null_stop_playback(ALCdevice *device)
|
||||
static void ALCnullBackend_stop(ALCnullBackend *self)
|
||||
{
|
||||
null_data *data = (null_data*)device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(!data->thread)
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
|
||||
data->killNow = 0;
|
||||
althrd_join(self->thread, &res);
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs null_funcs = {
|
||||
null_open_playback,
|
||||
null_close_playback,
|
||||
null_reset_playback,
|
||||
null_start_playback,
|
||||
null_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
typedef struct ALCnullBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCnullBackendFactory;
|
||||
#define ALCNULLBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCnullBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_null_init(BackendFuncs *func_list)
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCnullBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCnullBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCnullBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCnullBackendFactory factory = ALCNULLBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCnullBackendFactory_init(ALCnullBackendFactory* UNUSED(self))
|
||||
{
|
||||
*func_list = null_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_null_deinit(void)
|
||||
static ALCboolean ALCnullBackendFactory_querySupport(ALCnullBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_null_probe(enum DevProbe type)
|
||||
static void ALCnullBackendFactory_probe(ALCnullBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDeviceList(nullDevice);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, nullDevice, nullDevice+sizeof(nullDevice));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCnullBackendFactory_createBackend(ALCnullBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCnullBackend *backend;
|
||||
NEW_OBJ(backend, ALCnullBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+847
-216
File diff suppressed because it is too large
Load Diff
+591
-252
File diff suppressed because it is too large
Load Diff
+261
-175
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -23,9 +23,14 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
@@ -35,7 +40,7 @@ static const ALCchar pa_device[] = "PortAudio Default";
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *pa_handle;
|
||||
#define MAKE_FUNC(x) static typeof(x) * p##x
|
||||
#define MAKE_FUNC(x) static __typeof(x) * p##x
|
||||
MAKE_FUNC(Pa_Initialize);
|
||||
MAKE_FUNC(Pa_Terminate);
|
||||
MAKE_FUNC(Pa_GetErrorText);
|
||||
@@ -44,6 +49,7 @@ MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#undef MAKE_FUNC
|
||||
|
||||
@@ -55,6 +61,7 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
||||
#define Pa_OpenStream pPa_OpenStream
|
||||
#define Pa_CloseStream pPa_CloseStream
|
||||
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
||||
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
||||
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
||||
#endif
|
||||
|
||||
@@ -96,6 +103,7 @@ static ALCboolean pa_load(void)
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
@@ -118,157 +126,165 @@ static ALCboolean pa_load(void)
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
typedef struct ALCportPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
ALuint update_size;
|
||||
} ALCportPlayback;
|
||||
|
||||
RingBuffer *ring;
|
||||
} pa_data;
|
||||
static int ALCportPlayback_WriteCallback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData);
|
||||
|
||||
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device);
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self);
|
||||
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name);
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self);
|
||||
static ALCboolean ALCportPlayback_start(ALCportPlayback *self);
|
||||
static void ALCportPlayback_stop(ALCportPlayback *self);
|
||||
static DECLARE_FORWARD2(ALCportPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCportPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCportPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCportPlayback);
|
||||
|
||||
|
||||
static int pa_callback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData)
|
||||
static void ALCportPlayback_Construct(ALCportPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCportPlayback, ALCbackend, self);
|
||||
|
||||
(void)inputBuffer;
|
||||
(void)timeInfo;
|
||||
(void)statusFlags;
|
||||
|
||||
aluMixData(device, outputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
self->stream = NULL;
|
||||
}
|
||||
|
||||
static int pa_capture_cb(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData)
|
||||
static void ALCportPlayback_Destruct(ALCportPlayback *self)
|
||||
{
|
||||
ALCdevice *device = (ALCdevice*)userData;
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
(void)outputBuffer;
|
||||
(void)timeInfo;
|
||||
(void)statusFlags;
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
WriteRingBuffer(data->ring, inputBuffer, framesPerBuffer);
|
||||
|
||||
static int ALCportPlayback_WriteCallback(const void *UNUSED(inputBuffer), void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCportPlayback *self = userData;
|
||||
|
||||
ALCportPlayback_lock(self);
|
||||
aluMixData(STATIC_CAST(ALCbackend, self)->mDevice, outputBuffer, framesPerBuffer);
|
||||
ALCportPlayback_unlock(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
static ALCenum ALCportPlayback_open(ALCportPlayback *self, const ALCchar *name)
|
||||
{
|
||||
pa_data *data;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
data->update_size = device->UpdateSize;
|
||||
self->update_size = device->UpdateSize;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "device", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultOutputDevice();
|
||||
data->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
self->params.device = -1;
|
||||
if(!ConfigValueInt(NULL, "port", "device", &self->params.device) ||
|
||||
self->params.device < 0)
|
||||
self->params.device = Pa_GetDefaultOutputDevice();
|
||||
self->params.suggestedLatency = (device->UpdateSize*device->NumUpdates) /
|
||||
(float)device->Frequency;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
self->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
data->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
self->params.channelCount = ((device->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
self->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
self->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
self->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
self->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
self->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
err = Pa_OpenStream(&data->stream, NULL, &data->params, device->Frequency,
|
||||
device->UpdateSize, paNoFlag, pa_callback, device);
|
||||
err = Pa_OpenStream(&self->stream, NULL, &self->params,
|
||||
device->Frequency, device->UpdateSize, paNoFlag,
|
||||
ALCportPlayback_WriteCallback, self
|
||||
);
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(data->params.sampleFormat == paFloat32)
|
||||
if(self->params.sampleFormat == paFloat32)
|
||||
{
|
||||
data->params.sampleFormat = paInt16;
|
||||
self->params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->ExtraData = data;
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
}
|
||||
|
||||
static void pa_close_playback(ALCdevice *device)
|
||||
static ALCboolean ALCportPlayback_reset(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
const PaStreamInfo *streamInfo;
|
||||
|
||||
streamInfo = Pa_GetStreamInfo(data->stream);
|
||||
streamInfo = Pa_GetStreamInfo(self->stream);
|
||||
device->Frequency = streamInfo->sampleRate;
|
||||
device->UpdateSize = data->update_size;
|
||||
device->UpdateSize = self->update_size;
|
||||
|
||||
if(data->params.sampleFormat == paInt8)
|
||||
if(self->params.sampleFormat == paInt8)
|
||||
device->FmtType = DevFmtByte;
|
||||
else if(data->params.sampleFormat == paUInt8)
|
||||
else if(self->params.sampleFormat == paUInt8)
|
||||
device->FmtType = DevFmtUByte;
|
||||
else if(data->params.sampleFormat == paInt16)
|
||||
else if(self->params.sampleFormat == paInt16)
|
||||
device->FmtType = DevFmtShort;
|
||||
else if(data->params.sampleFormat == paInt32)
|
||||
else if(self->params.sampleFormat == paInt32)
|
||||
device->FmtType = DevFmtInt;
|
||||
else if(data->params.sampleFormat == paFloat32)
|
||||
else if(self->params.sampleFormat == paFloat32)
|
||||
device->FmtType = DevFmtFloat;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected sample format: 0x%lx\n", data->params.sampleFormat);
|
||||
ERR("Unexpected sample format: 0x%lx\n", self->params.sampleFormat);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(data->params.channelCount == 2)
|
||||
if(self->params.channelCount == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else if(data->params.channelCount == 1)
|
||||
else if(self->params.channelCount == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected channel count: %u\n", data->params.channelCount);
|
||||
ERR("Unexpected channel count: %u\n", self->params.channelCount);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
SetDefaultChannelOrder(device);
|
||||
@@ -276,12 +292,11 @@ static ALCboolean pa_reset_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean pa_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCportPlayback_start(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
err = Pa_StartStream(self->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_StartStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
@@ -291,157 +306,197 @@ static ALCboolean pa_start_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_stop_playback(ALCdevice *device)
|
||||
static void ALCportPlayback_stop(ALCportPlayback *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
PaError err = Pa_StopStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
||||
static ALCenum pa_open_capture(ALCdevice *device, const ALCchar *deviceName)
|
||||
typedef struct ALCportCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
PaStream *stream;
|
||||
PaStreamParameters params;
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
} ALCportCapture;
|
||||
|
||||
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData);
|
||||
|
||||
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device);
|
||||
static void ALCportCapture_Destruct(ALCportCapture *self);
|
||||
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name);
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean ALCportCapture_start(ALCportCapture *self);
|
||||
static void ALCportCapture_stop(ALCportCapture *self);
|
||||
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples);
|
||||
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self);
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCportCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCportCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCportCapture);
|
||||
|
||||
|
||||
static void ALCportCapture_Construct(ALCportCapture *self, ALCdevice *device)
|
||||
{
|
||||
ALuint frame_size;
|
||||
pa_data *data;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCportCapture, ALCbackend, self);
|
||||
|
||||
self->stream = NULL;
|
||||
self->ring = NULL;
|
||||
}
|
||||
|
||||
static void ALCportCapture_Destruct(ALCportCapture *self)
|
||||
{
|
||||
PaError err = self->stream ? Pa_CloseStream(self->stream) : paNoError;
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
self->stream = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCportCapture_ReadCallback(const void *inputBuffer, void *UNUSED(outputBuffer),
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *UNUSED(timeInfo),
|
||||
const PaStreamCallbackFlags UNUSED(statusFlags), void *userData)
|
||||
{
|
||||
ALCportCapture *self = userData;
|
||||
size_t writable = ll_ringbuffer_write_space(self->ring);
|
||||
|
||||
if(framesPerBuffer > writable)
|
||||
framesPerBuffer = writable;
|
||||
ll_ringbuffer_write(self->ring, inputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum ALCportCapture_open(ALCportCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALuint samples, frame_size;
|
||||
PaError err;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = pa_device;
|
||||
else if(strcmp(deviceName, pa_device) != 0)
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (pa_data*)calloc(1, sizeof(pa_data));
|
||||
if(data == NULL)
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
samples = device->UpdateSize * device->NumUpdates;
|
||||
samples = maxu(samples, 100 * device->Frequency / 1000);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->ring = CreateRingBuffer(frame_size, device->UpdateSize*device->NumUpdates);
|
||||
if(data->ring == NULL)
|
||||
goto error;
|
||||
self->ring = ll_ringbuffer_create(samples, frame_size, false);
|
||||
if(self->ring == NULL) return ALC_INVALID_VALUE;
|
||||
|
||||
data->params.device = -1;
|
||||
if(!ConfigValueInt("port", "capture", &data->params.device) ||
|
||||
data->params.device < 0)
|
||||
data->params.device = Pa_GetDefaultOutputDevice();
|
||||
data->params.suggestedLatency = 0.0f;
|
||||
data->params.hostApiSpecificStreamInfo = NULL;
|
||||
self->params.device = -1;
|
||||
if(!ConfigValueInt(NULL, "port", "capture", &self->params.device) ||
|
||||
self->params.device < 0)
|
||||
self->params.device = Pa_GetDefaultInputDevice();
|
||||
self->params.suggestedLatency = 0.0f;
|
||||
self->params.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
data->params.sampleFormat = paInt8;
|
||||
self->params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
data->params.sampleFormat = paUInt8;
|
||||
self->params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
data->params.sampleFormat = paInt16;
|
||||
self->params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
data->params.sampleFormat = paInt32;
|
||||
self->params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
data->params.sampleFormat = paFloat32;
|
||||
self->params.sampleFormat = paFloat32;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
case DevFmtUShort:
|
||||
ERR("%s samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
goto error;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
data->params.channelCount = ChannelsFromDevFmt(device->FmtChans);
|
||||
self->params.channelCount = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
|
||||
err = Pa_OpenStream(&data->stream, &data->params, NULL, device->Frequency,
|
||||
paFramesPerBufferUnspecified, paNoFlag, pa_capture_cb, device);
|
||||
err = Pa_OpenStream(&self->stream, &self->params, NULL,
|
||||
device->Frequency, paFramesPerBufferUnspecified, paNoFlag,
|
||||
ALCportCapture_ReadCallback, self
|
||||
);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_OpenStream() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
goto error;
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
device->ExtraData = data;
|
||||
return ALC_NO_ERROR;
|
||||
|
||||
error:
|
||||
DestroyRingBuffer(data->ring);
|
||||
free(data);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
static void pa_close_capture(ALCdevice *device)
|
||||
|
||||
static ALCboolean ALCportCapture_start(ALCportCapture *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_CloseStream(data->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error closing stream: %s\n", Pa_GetErrorText(err));
|
||||
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static void pa_start_capture(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StartStream(data->stream);
|
||||
PaError err = Pa_StartStream(self->stream);
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Error starting stream: %s\n", Pa_GetErrorText(err));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void pa_stop_capture(ALCdevice *device)
|
||||
static void ALCportCapture_stop(ALCportCapture *self)
|
||||
{
|
||||
pa_data *data = (pa_data*)device->ExtraData;
|
||||
PaError err;
|
||||
|
||||
err = Pa_StopStream(data->stream);
|
||||
PaError err = Pa_StopStream(self->stream);
|
||||
if(err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
static ALCenum pa_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples)
|
||||
|
||||
static ALCuint ALCportCapture_availableSamples(ALCportCapture *self)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
ReadRingBuffer(data->ring, buffer, samples);
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
static ALCenum ALCportCapture_captureSamples(ALCportCapture *self, ALCvoid *buffer, ALCuint samples)
|
||||
{
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint pa_available_samples(ALCdevice *device)
|
||||
{
|
||||
pa_data *data = device->ExtraData;
|
||||
return RingBufferSize(data->ring);
|
||||
}
|
||||
|
||||
typedef struct ALCportBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCportBackendFactory;
|
||||
#define ALCPORTBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCportBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory *self);
|
||||
static void ALCportBackendFactory_deinit(ALCportBackendFactory *self);
|
||||
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCportBackendFactory);
|
||||
|
||||
|
||||
static const BackendFuncs pa_funcs = {
|
||||
pa_open_playback,
|
||||
pa_close_playback,
|
||||
pa_reset_playback,
|
||||
pa_start_playback,
|
||||
pa_stop_playback,
|
||||
pa_open_capture,
|
||||
pa_close_capture,
|
||||
pa_start_capture,
|
||||
pa_stop_capture,
|
||||
pa_capture_samples,
|
||||
pa_available_samples
|
||||
};
|
||||
|
||||
ALCboolean alc_pa_init(BackendFuncs *func_list)
|
||||
static ALCboolean ALCportBackendFactory_init(ALCportBackendFactory* UNUSED(self))
|
||||
{
|
||||
if(!pa_load())
|
||||
return ALC_FALSE;
|
||||
*func_list = pa_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_pa_deinit(void)
|
||||
static void ALCportBackendFactory_deinit(ALCportBackendFactory* UNUSED(self))
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(pa_handle)
|
||||
@@ -455,15 +510,46 @@ void alc_pa_deinit(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void alc_pa_probe(enum DevProbe type)
|
||||
static ALCboolean ALCportBackendFactory_querySupport(ALCportBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCportBackendFactory_probe(ALCportBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDeviceList(pa_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
AppendCaptureDeviceList(pa_device);
|
||||
alstr_append_range(outnames, pa_device, pa_device+sizeof(pa_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCportBackendFactory_createBackend(ALCportBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCportPlayback *backend;
|
||||
NEW_OBJ(backend, ALCportPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
ALCportCapture *backend;
|
||||
NEW_OBJ(backend, ALCportCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ALCbackendFactory *ALCportBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCportBackendFactory factory = ALCPORTBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
+1321
-895
File diff suppressed because it is too large
Load Diff
+1068
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2018 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DEVNAME_PREFIX "OpenAL Soft on "
|
||||
#else
|
||||
#define DEVNAME_PREFIX ""
|
||||
#endif
|
||||
|
||||
typedef struct ALCsdl2Backend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
SDL_AudioDeviceID deviceID;
|
||||
ALsizei frameSize;
|
||||
|
||||
ALuint Frequency;
|
||||
enum DevFmtChannels FmtChans;
|
||||
enum DevFmtType FmtType;
|
||||
ALuint UpdateSize;
|
||||
} ALCsdl2Backend;
|
||||
|
||||
static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device);
|
||||
static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self);
|
||||
static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name);
|
||||
static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self);
|
||||
static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self);
|
||||
static void ALCsdl2Backend_stop(ALCsdl2Backend *self);
|
||||
static DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static void ALCsdl2Backend_lock(ALCsdl2Backend *self);
|
||||
static void ALCsdl2Backend_unlock(ALCsdl2Backend *self);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend);
|
||||
|
||||
static const ALCchar defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
||||
|
||||
static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsdl2Backend, ALCbackend, self);
|
||||
|
||||
self->deviceID = 0;
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
}
|
||||
|
||||
static void ALCsdl2Backend_Destruct(ALCsdl2Backend *self)
|
||||
{
|
||||
if(self->deviceID)
|
||||
SDL_CloseAudioDevice(self->deviceID);
|
||||
self->deviceID = 0;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static void ALCsdl2Backend_audioCallback(void *ptr, Uint8 *stream, int len)
|
||||
{
|
||||
ALCsdl2Backend *self = (ALCsdl2Backend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
assert((len % self->frameSize) == 0);
|
||||
aluMixData(device, stream, len / self->frameSize);
|
||||
}
|
||||
|
||||
static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
SDL_AudioSpec want, have;
|
||||
|
||||
SDL_zero(want);
|
||||
SDL_zero(have);
|
||||
|
||||
want.freq = device->Frequency;
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtUByte: want.format = AUDIO_U8; break;
|
||||
case DevFmtByte: want.format = AUDIO_S8; break;
|
||||
case DevFmtUShort: want.format = AUDIO_U16SYS; break;
|
||||
case DevFmtShort: want.format = AUDIO_S16SYS; break;
|
||||
case DevFmtUInt: /* fall-through */
|
||||
case DevFmtInt: want.format = AUDIO_S32SYS; break;
|
||||
case DevFmtFloat: want.format = AUDIO_F32; break;
|
||||
}
|
||||
want.channels = (device->FmtChans == DevFmtMono) ? 1 : 2;
|
||||
want.samples = device->UpdateSize;
|
||||
want.callback = ALCsdl2Backend_audioCallback;
|
||||
want.userdata = self;
|
||||
|
||||
/* Passing NULL to SDL_OpenAudioDevice opens a default, which isn't
|
||||
* necessarily the first in the list.
|
||||
*/
|
||||
if(!name || strcmp(name, defaultDeviceName) == 0)
|
||||
self->deviceID = SDL_OpenAudioDevice(NULL, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
else
|
||||
{
|
||||
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
||||
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
|
||||
self->deviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
else
|
||||
self->deviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
}
|
||||
if(self->deviceID == 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
device->Frequency = have.freq;
|
||||
if(have.channels == 1)
|
||||
device->FmtChans = DevFmtMono;
|
||||
else if(have.channels == 2)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
else
|
||||
{
|
||||
ERR("Got unhandled SDL channel count: %d\n", (int)have.channels);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
switch(have.format)
|
||||
{
|
||||
case AUDIO_U8: device->FmtType = DevFmtUByte; break;
|
||||
case AUDIO_S8: device->FmtType = DevFmtByte; break;
|
||||
case AUDIO_U16SYS: device->FmtType = DevFmtUShort; break;
|
||||
case AUDIO_S16SYS: device->FmtType = DevFmtShort; break;
|
||||
case AUDIO_S32SYS: device->FmtType = DevFmtInt; break;
|
||||
case AUDIO_F32SYS: device->FmtType = DevFmtFloat; break;
|
||||
default:
|
||||
ERR("Got unsupported SDL format: 0x%04x\n", have.format);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
device->UpdateSize = have.samples;
|
||||
device->NumUpdates = 2; /* SDL always (tries to) use two periods. */
|
||||
|
||||
self->frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
self->Frequency = device->Frequency;
|
||||
self->FmtChans = device->FmtChans;
|
||||
self->FmtType = device->FmtType;
|
||||
self->UpdateSize = device->UpdateSize;
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name ? name : defaultDeviceName);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
device->Frequency = self->Frequency;
|
||||
device->FmtChans = self->FmtChans;
|
||||
device->FmtType = self->FmtType;
|
||||
device->UpdateSize = self->UpdateSize;
|
||||
device->NumUpdates = 2;
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self)
|
||||
{
|
||||
SDL_PauseAudioDevice(self->deviceID, 0);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void ALCsdl2Backend_stop(ALCsdl2Backend *self)
|
||||
{
|
||||
SDL_PauseAudioDevice(self->deviceID, 1);
|
||||
}
|
||||
|
||||
static void ALCsdl2Backend_lock(ALCsdl2Backend *self)
|
||||
{
|
||||
SDL_LockAudioDevice(self->deviceID);
|
||||
}
|
||||
|
||||
static void ALCsdl2Backend_unlock(ALCsdl2Backend *self)
|
||||
{
|
||||
SDL_UnlockAudioDevice(self->deviceID);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALCsdl2BackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsdl2BackendFactory;
|
||||
#define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self);
|
||||
static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory *self);
|
||||
static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCsdl2BackendFactory factory = ALCsdl2BACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory* UNUSED(self))
|
||||
{
|
||||
if(SDL_InitSubSystem(SDL_INIT_AUDIO) == 0)
|
||||
return AL_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsdl2BackendFactory_deinit(ALCsdl2BackendFactory* UNUSED(self))
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
int num_devices, i;
|
||||
al_string name;
|
||||
|
||||
if(type != ALL_DEVICE_PROBE)
|
||||
return;
|
||||
|
||||
AL_STRING_INIT(name);
|
||||
num_devices = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
|
||||
alstr_append_range(outnames, defaultDeviceName, defaultDeviceName+sizeof(defaultDeviceName));
|
||||
for(i = 0;i < num_devices;++i)
|
||||
{
|
||||
alstr_copy_cstr(&name, DEVNAME_PREFIX);
|
||||
alstr_append_cstr(&name, SDL_GetAudioDeviceName(i, SDL_FALSE));
|
||||
if(!alstr_empty(name))
|
||||
alstr_append_range(outnames, VECTOR_BEGIN(name), VECTOR_END(name)+1);
|
||||
}
|
||||
alstr_reset(&name);
|
||||
}
|
||||
|
||||
static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCsdl2Backend *backend;
|
||||
NEW_OBJ(backend, ALCsdl2Backend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
+400
-172
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "threads.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sndio.h>
|
||||
|
||||
@@ -33,123 +37,89 @@
|
||||
static const ALCchar sndio_device[] = "SndIO Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
static void *sndio_handle;
|
||||
#define MAKE_FUNC(x) static typeof(x) * p##x
|
||||
MAKE_FUNC(sio_initpar);
|
||||
MAKE_FUNC(sio_open);
|
||||
MAKE_FUNC(sio_close);
|
||||
MAKE_FUNC(sio_setpar);
|
||||
MAKE_FUNC(sio_getpar);
|
||||
MAKE_FUNC(sio_getcap);
|
||||
MAKE_FUNC(sio_onmove);
|
||||
MAKE_FUNC(sio_write);
|
||||
MAKE_FUNC(sio_read);
|
||||
MAKE_FUNC(sio_start);
|
||||
MAKE_FUNC(sio_stop);
|
||||
MAKE_FUNC(sio_nfds);
|
||||
MAKE_FUNC(sio_pollfd);
|
||||
MAKE_FUNC(sio_revents);
|
||||
MAKE_FUNC(sio_eof);
|
||||
MAKE_FUNC(sio_setvol);
|
||||
MAKE_FUNC(sio_onvol);
|
||||
typedef struct SndioPlayback {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
#define sio_initpar psio_initpar
|
||||
#define sio_open psio_open
|
||||
#define sio_close psio_close
|
||||
#define sio_setpar psio_setpar
|
||||
#define sio_getpar psio_getpar
|
||||
#define sio_getcap psio_getcap
|
||||
#define sio_onmove psio_onmove
|
||||
#define sio_write psio_write
|
||||
#define sio_read psio_read
|
||||
#define sio_start psio_start
|
||||
#define sio_stop psio_stop
|
||||
#define sio_nfds psio_nfds
|
||||
#define sio_pollfd psio_pollfd
|
||||
#define sio_revents psio_revents
|
||||
#define sio_eof psio_eof
|
||||
#define sio_setvol psio_setvol
|
||||
#define sio_onvol psio_onvol
|
||||
#endif
|
||||
|
||||
|
||||
static ALCboolean sndio_load(void)
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!sndio_handle)
|
||||
{
|
||||
sndio_handle = LoadLib("libsndio.so");
|
||||
if(!sndio_handle)
|
||||
return ALC_FALSE;
|
||||
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = GetSymbol(sndio_handle, #f); \
|
||||
if(p##f == NULL) { \
|
||||
CloseLib(sndio_handle); \
|
||||
sndio_handle = NULL; \
|
||||
return ALC_FALSE; \
|
||||
} \
|
||||
} while(0)
|
||||
LOAD_FUNC(sio_initpar);
|
||||
LOAD_FUNC(sio_open);
|
||||
LOAD_FUNC(sio_close);
|
||||
LOAD_FUNC(sio_setpar);
|
||||
LOAD_FUNC(sio_getpar);
|
||||
LOAD_FUNC(sio_getcap);
|
||||
LOAD_FUNC(sio_onmove);
|
||||
LOAD_FUNC(sio_write);
|
||||
LOAD_FUNC(sio_read);
|
||||
LOAD_FUNC(sio_start);
|
||||
LOAD_FUNC(sio_stop);
|
||||
LOAD_FUNC(sio_nfds);
|
||||
LOAD_FUNC(sio_pollfd);
|
||||
LOAD_FUNC(sio_revents);
|
||||
LOAD_FUNC(sio_eof);
|
||||
LOAD_FUNC(sio_setvol);
|
||||
LOAD_FUNC(sio_onvol);
|
||||
#undef LOAD_FUNC
|
||||
}
|
||||
#endif
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
ALvoid *mix_data;
|
||||
ALsizei data_size;
|
||||
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} sndio_data;
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} SndioPlayback;
|
||||
|
||||
static int SndioPlayback_mixerProc(void *ptr);
|
||||
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device);
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self);
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name);
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self);
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self);
|
||||
static void SndioPlayback_stop(SndioPlayback *self);
|
||||
static DECLARE_FORWARD2(SndioPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(SndioPlayback, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(SndioPlayback)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioPlayback);
|
||||
|
||||
|
||||
static ALuint sndio_proc(ALvoid *ptr)
|
||||
static void SndioPlayback_Construct(SndioPlayback *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *device = ptr;
|
||||
sndio_data *data = device->ExtraData;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(SndioPlayback, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void SndioPlayback_Destruct(SndioPlayback *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
|
||||
al_free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int SndioPlayback_mixerProc(void *ptr)
|
||||
{
|
||||
SndioPlayback *self = (SndioPlayback*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
size_t wrote;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!data->killNow && device->Connected)
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ALsizei len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
ALsizei len = self->data_size;
|
||||
ALubyte *WritePtr = self->mix_data;
|
||||
|
||||
SndioPlayback_lock(self);
|
||||
aluMixData(device, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
SndioPlayback_unlock(self);
|
||||
while(len > 0 && !ATOMIC_LOAD(&self->killNow, almemory_order_acquire))
|
||||
{
|
||||
wrote = sio_write(data->sndHandle, WritePtr, len);
|
||||
wrote = sio_write(self->sndHandle, WritePtr, len);
|
||||
if(wrote == 0)
|
||||
{
|
||||
ERR("sio_write failed\n");
|
||||
aluHandleDisconnect(device);
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device, "Failed to write playback samples");
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -162,45 +132,30 @@ static ALuint sndio_proc(ALvoid *ptr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ALCenum sndio_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
static ALCenum SndioPlayback_open(SndioPlayback *self, const ALCchar *name)
|
||||
{
|
||||
sndio_data *data;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = sndio_device;
|
||||
else if(strcmp(deviceName, sndio_device) != 0)
|
||||
if(!name)
|
||||
name = sndio_device;
|
||||
else if(strcmp(name, sndio_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = calloc(1, sizeof(*data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->sndHandle = sio_open(NULL, SIO_PLAY, 0);
|
||||
if(data->sndHandle == NULL)
|
||||
self->sndHandle = sio_open(NULL, SIO_PLAY, 0);
|
||||
if(self->sndHandle == NULL)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open device\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void sndio_close_playback(ALCdevice *device)
|
||||
static ALCboolean SndioPlayback_reset(SndioPlayback *self)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
|
||||
sio_close(data->sndHandle);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean sndio_reset_playback(ALCdevice *device)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
|
||||
sio_initpar(&par);
|
||||
@@ -242,7 +197,7 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
|
||||
par.appbufsz = device->UpdateSize * (device->NumUpdates-1);
|
||||
if(!par.appbufsz) par.appbufsz = device->UpdateSize;
|
||||
|
||||
if(!sio_setpar(data->sndHandle, &par) || !sio_getpar(data->sndHandle, &par))
|
||||
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return ALC_FALSE;
|
||||
@@ -283,90 +238,363 @@ static ALCboolean sndio_reset_playback(ALCdevice *device)
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean sndio_start_playback(ALCdevice *device)
|
||||
static ALCboolean SndioPlayback_start(SndioPlayback *self)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
|
||||
if(!sio_start(data->sndHandle))
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
al_free(self->mix_data);
|
||||
self->mix_data = al_calloc(16, self->data_size);
|
||||
|
||||
if(!sio_start(self->sndHandle))
|
||||
{
|
||||
ERR("Error starting playback\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->thread = StartThread(sndio_proc, device);
|
||||
if(data->thread == NULL)
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, SndioPlayback_mixerProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(data->sndHandle);
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void sndio_stop_playback(ALCdevice *device)
|
||||
static void SndioPlayback_stop(SndioPlayback *self)
|
||||
{
|
||||
sndio_data *data = device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(!data->thread)
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
|
||||
data->killNow = 0;
|
||||
if(!sio_stop(data->sndHandle))
|
||||
if(!sio_stop(self->sndHandle))
|
||||
ERR("Error stopping device\n");
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
al_free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs sndio_funcs = {
|
||||
sndio_open_playback,
|
||||
sndio_close_playback,
|
||||
sndio_reset_playback,
|
||||
sndio_start_playback,
|
||||
sndio_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
typedef struct SndioCapture {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
ALCboolean alc_sndio_init(BackendFuncs *func_list)
|
||||
struct sio_hdl *sndHandle;
|
||||
|
||||
ll_ringbuffer_t *ring;
|
||||
|
||||
ATOMIC(int) killNow;
|
||||
althrd_t thread;
|
||||
} SndioCapture;
|
||||
|
||||
static int SndioCapture_recordProc(void *ptr);
|
||||
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device);
|
||||
static void SndioCapture_Destruct(SndioCapture *self);
|
||||
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name);
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, ALCboolean, reset)
|
||||
static ALCboolean SndioCapture_start(SndioCapture *self);
|
||||
static void SndioCapture_stop(SndioCapture *self);
|
||||
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples);
|
||||
static ALCuint SndioCapture_availableSamples(SndioCapture *self);
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(SndioCapture, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(SndioCapture)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(SndioCapture);
|
||||
|
||||
|
||||
static void SndioCapture_Construct(SndioCapture *self, ALCdevice *device)
|
||||
{
|
||||
if(!sndio_load())
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(SndioCapture, ALCbackend, self);
|
||||
|
||||
self->sndHandle = NULL;
|
||||
self->ring = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void SndioCapture_Destruct(SndioCapture *self)
|
||||
{
|
||||
if(self->sndHandle)
|
||||
sio_close(self->sndHandle);
|
||||
self->sndHandle = NULL;
|
||||
|
||||
ll_ringbuffer_free(self->ring);
|
||||
self->ring = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int SndioCapture_recordProc(void* ptr)
|
||||
{
|
||||
SndioCapture *self = (SndioCapture*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALsizei frameSize;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), RECORD_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ll_ringbuffer_data_t data[2];
|
||||
size_t total, todo;
|
||||
|
||||
ll_ringbuffer_get_write_vector(self->ring, data);
|
||||
todo = data[0].len + data[1].len;
|
||||
if(todo == 0)
|
||||
{
|
||||
static char junk[4096];
|
||||
sio_read(self->sndHandle, junk, minz(sizeof(junk)/frameSize, device->UpdateSize)*frameSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
total = 0;
|
||||
data[0].len *= frameSize;
|
||||
data[1].len *= frameSize;
|
||||
todo = minz(todo, device->UpdateSize) * frameSize;
|
||||
while(total < todo)
|
||||
{
|
||||
size_t got;
|
||||
|
||||
if(!data[0].len)
|
||||
data[0] = data[1];
|
||||
|
||||
got = sio_read(self->sndHandle, data[0].buf, minz(todo-total, data[0].len));
|
||||
if(!got)
|
||||
{
|
||||
SndioCapture_lock(self);
|
||||
aluHandleDisconnect(device, "Failed to read capture samples");
|
||||
SndioCapture_unlock(self);
|
||||
break;
|
||||
}
|
||||
|
||||
data[0].buf += got;
|
||||
data[0].len -= got;
|
||||
total += got;
|
||||
}
|
||||
ll_ringbuffer_write_advance(self->ring, total / frameSize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum SndioCapture_open(SndioCapture *self, const ALCchar *name)
|
||||
{
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
struct sio_par par;
|
||||
|
||||
if(!name)
|
||||
name = sndio_device;
|
||||
else if(strcmp(name, sndio_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
self->sndHandle = sio_open(NULL, SIO_REC, 0);
|
||||
if(self->sndHandle == NULL)
|
||||
{
|
||||
ERR("Could not open device\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
sio_initpar(&par);
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bps = 1;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bps = 1;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
par.bps = 2;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bps = 2;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtInt:
|
||||
par.bps = 4;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bps = 4;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
par.bits = par.bps * 8;
|
||||
par.le = SIO_LE_NATIVE;
|
||||
par.msb = SIO_LE_NATIVE ? 0 : 1;
|
||||
par.rchan = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
par.rate = device->Frequency;
|
||||
|
||||
par.appbufsz = maxu(device->UpdateSize*device->NumUpdates, (device->Frequency+9)/10);
|
||||
par.round = clampu(par.appbufsz/device->NumUpdates, (device->Frequency+99)/100,
|
||||
(device->Frequency+19)/20);
|
||||
|
||||
device->UpdateSize = par.round;
|
||||
device->NumUpdates = maxu(par.appbufsz/par.round, 1);
|
||||
|
||||
if(!sio_setpar(self->sndHandle, &par) || !sio_getpar(self->sndHandle, &par))
|
||||
{
|
||||
ERR("Failed to set device parameters\n");
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if(par.bits != par.bps*8)
|
||||
{
|
||||
ERR("Padded samples not supported (%u of %u bits)\n", par.bits, par.bps*8);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if(!((device->FmtType == DevFmtByte && par.bits == 8 && par.sig != 0) ||
|
||||
(device->FmtType == DevFmtUByte && par.bits == 8 && par.sig == 0) ||
|
||||
(device->FmtType == DevFmtShort && par.bits == 16 && par.sig != 0) ||
|
||||
(device->FmtType == DevFmtUShort && par.bits == 16 && par.sig == 0) ||
|
||||
(device->FmtType == DevFmtInt && par.bits == 32 && par.sig != 0) ||
|
||||
(device->FmtType == DevFmtUInt && par.bits == 32 && par.sig == 0)) ||
|
||||
ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)par.rchan ||
|
||||
device->Frequency != par.rate)
|
||||
{
|
||||
ERR("Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead\n",
|
||||
DevFmtTypeString(device->FmtType), DevFmtChannelsString(device->FmtChans),
|
||||
device->Frequency, par.sig?'s':'u', par.bits, par.rchan, par.rate);
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
self->ring = ll_ringbuffer_create(device->UpdateSize*device->NumUpdates, par.bps*par.rchan, 0);
|
||||
if(!self->ring)
|
||||
{
|
||||
ERR("Failed to allocate %u-byte ringbuffer\n",
|
||||
device->UpdateSize*device->NumUpdates*par.bps*par.rchan);
|
||||
return ALC_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCboolean SndioCapture_start(SndioCapture *self)
|
||||
{
|
||||
if(!sio_start(self->sndHandle))
|
||||
{
|
||||
ERR("Error starting playback\n");
|
||||
return ALC_FALSE;
|
||||
*func_list = sndio_funcs;
|
||||
}
|
||||
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, SndioCapture_recordProc, self) != althrd_success)
|
||||
{
|
||||
sio_stop(self->sndHandle);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_sndio_deinit(void)
|
||||
static void SndioCapture_stop(SndioCapture *self)
|
||||
{
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(sndio_handle)
|
||||
CloseLib(sndio_handle);
|
||||
sndio_handle = NULL;
|
||||
#endif
|
||||
int res;
|
||||
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
if(!sio_stop(self->sndHandle))
|
||||
ERR("Error stopping device\n");
|
||||
}
|
||||
|
||||
void alc_sndio_probe(enum DevProbe type)
|
||||
static ALCenum SndioCapture_captureSamples(SndioCapture *self, void *buffer, ALCuint samples)
|
||||
{
|
||||
ll_ringbuffer_read(self->ring, buffer, samples);
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static ALCuint SndioCapture_availableSamples(SndioCapture *self)
|
||||
{
|
||||
return ll_ringbuffer_read_space(self->ring);
|
||||
}
|
||||
|
||||
|
||||
typedef struct SndioBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} SndioBackendFactory;
|
||||
#define SNDIOBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(SndioBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean SndioBackendFactory_init(SndioBackendFactory *self);
|
||||
static DECLARE_FORWARD(SndioBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory *self, ALCbackend_Type type);
|
||||
static void SndioBackendFactory_probe(SndioBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(SndioBackendFactory);
|
||||
|
||||
ALCbackendFactory *SndioBackendFactory_getFactory(void)
|
||||
{
|
||||
static SndioBackendFactory factory = SNDIOBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
static ALCboolean SndioBackendFactory_init(SndioBackendFactory* UNUSED(self))
|
||||
{
|
||||
/* No dynamic loading */
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean SndioBackendFactory_querySupport(SndioBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback || type == ALCbackend_Capture)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
static void SndioBackendFactory_probe(SndioBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDeviceList(sndio_device);
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
alstr_append_range(outnames, sndio_device, sndio_device+sizeof(sndio_device));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* SndioBackendFactory_createBackend(SndioBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
SndioPlayback *backend;
|
||||
NEW_OBJ(backend, SndioPlayback)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
if(type == ALCbackend_Capture)
|
||||
{
|
||||
SndioCapture *backend;
|
||||
NEW_OBJ(backend, SndioCapture)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+187
-109
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
@@ -30,109 +31,174 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include "backends/base.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
typedef struct ALCsolarisBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
int fd;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCsolarisBackend;
|
||||
|
||||
static int ALCsolarisBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device);
|
||||
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self);
|
||||
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name);
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self);
|
||||
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self);
|
||||
static void ALCsolarisBackend_stop(ALCsolarisBackend *self);
|
||||
static DECLARE_FORWARD2(ALCsolarisBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCsolarisBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCsolarisBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCsolarisBackend);
|
||||
|
||||
|
||||
static const ALCchar solaris_device[] = "Solaris Default";
|
||||
|
||||
static const char *solaris_driver = "/dev/audio";
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
|
||||
ALubyte *mix_data;
|
||||
int data_size;
|
||||
} solaris_data;
|
||||
|
||||
|
||||
static ALuint SolarisProc(ALvoid *ptr)
|
||||
static void ALCsolarisBackend_Construct(ALCsolarisBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
solaris_data *data = (solaris_data*)pDevice->ExtraData;
|
||||
ALint frameSize;
|
||||
int wrote;
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCsolarisBackend, ALCbackend, self);
|
||||
|
||||
self->fd = -1;
|
||||
self->mix_data = NULL;
|
||||
ATOMIC_INIT(&self->killNow, AL_FALSE);
|
||||
}
|
||||
|
||||
static void ALCsolarisBackend_Destruct(ALCsolarisBackend *self)
|
||||
{
|
||||
if(self->fd != -1)
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
|
||||
free(self->mix_data);
|
||||
self->mix_data = NULL;
|
||||
self->data_size = 0;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
|
||||
static int ALCsolarisBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCsolarisBackend *self = ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct timeval timeout;
|
||||
ALubyte *write_ptr;
|
||||
ALint frame_size;
|
||||
ALint to_write;
|
||||
ssize_t wrote;
|
||||
fd_set wfds;
|
||||
int sret;
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
|
||||
frame_size = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
while(!data->killNow && pDevice->Connected)
|
||||
ALCsolarisBackend_lock(self);
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
ALint len = data->data_size;
|
||||
ALubyte *WritePtr = data->mix_data;
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(self->fd, &wfds);
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
aluMixData(pDevice, WritePtr, len/frameSize);
|
||||
while(len > 0 && !data->killNow)
|
||||
ALCsolarisBackend_unlock(self);
|
||||
sret = select(self->fd+1, NULL, &wfds, NULL, &timeout);
|
||||
ALCsolarisBackend_lock(self);
|
||||
if(sret < 0)
|
||||
{
|
||||
wrote = write(data->fd, WritePtr, len);
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
ERR("select failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device, "Failed to wait for playback buffer: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
{
|
||||
WARN("select timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
write_ptr = self->mix_data;
|
||||
to_write = self->data_size;
|
||||
aluMixData(device, write_ptr, to_write/frame_size);
|
||||
while(to_write > 0 && !ATOMIC_LOAD_SEQ(&self->killNow))
|
||||
{
|
||||
wrote = write(self->fd, write_ptr, to_write);
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
|
||||
{
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(pDevice);
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep(1);
|
||||
continue;
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
aluHandleDisconnect(device, "Failed to write playback samples: %s",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
len -= wrote;
|
||||
WritePtr += wrote;
|
||||
to_write -= wrote;
|
||||
write_ptr += wrote;
|
||||
}
|
||||
}
|
||||
ALCsolarisBackend_unlock(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ALCenum solaris_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
static ALCenum ALCsolarisBackend_open(ALCsolarisBackend *self, const ALCchar *name)
|
||||
{
|
||||
solaris_data *data;
|
||||
ALCdevice *device;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = solaris_device;
|
||||
else if(strcmp(deviceName, solaris_device) != 0)
|
||||
if(!name)
|
||||
name = solaris_device;
|
||||
else if(strcmp(name, solaris_device) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (solaris_data*)calloc(1, sizeof(solaris_data));
|
||||
data->killNow = 0;
|
||||
|
||||
data->fd = open(solaris_driver, O_WRONLY);
|
||||
if(data->fd == -1)
|
||||
self->fd = open(solaris_driver, O_WRONLY);
|
||||
if(self->fd == -1)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open %s: %s\n", solaris_driver, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void solaris_close_playback(ALCdevice *device)
|
||||
static ALCboolean ALCsolarisBackend_reset(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
close(data->fd);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
|
||||
audio_info_t info;
|
||||
ALuint frameSize;
|
||||
int numChannels;
|
||||
ALsizei frameSize;
|
||||
ALsizei numChannels;
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
@@ -140,7 +206,7 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
|
||||
if(device->FmtChans != DevFmtMono)
|
||||
device->FmtChans = DevFmtStereo;
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans);
|
||||
numChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
info.play.channels = numChannels;
|
||||
|
||||
switch(device->FmtType)
|
||||
@@ -168,15 +234,15 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
frameSize = numChannels * BytesFromDevFmt(device->FmtType);
|
||||
info.play.buffer_size = device->UpdateSize*device->NumUpdates * frameSize;
|
||||
|
||||
if(ioctl(data->fd, AUDIO_SETINFO, &info) < 0)
|
||||
if(ioctl(self->fd, AUDIO_SETINFO, &info) < 0)
|
||||
{
|
||||
ERR("ioctl failed: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
if(ChannelsFromDevFmt(device->FmtChans) != info.play.channels)
|
||||
if(ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder) != (ALsizei)info.play.channels)
|
||||
{
|
||||
ERR("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device->FmtChans), info.play.channels);
|
||||
ERR("Failed to set %s, got %u channels instead\n", DevFmtChannelsString(device->FmtChans), info.play.channels);
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
@@ -195,74 +261,73 @@ static ALCboolean solaris_reset_playback(ALCdevice *device)
|
||||
|
||||
SetDefaultChannelOrder(device);
|
||||
|
||||
free(self->mix_data);
|
||||
self->data_size = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
self->mix_data = calloc(1, self->data_size);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean solaris_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCsolarisBackend_start(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
|
||||
data->data_size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->mix_data = calloc(1, data->data_size);
|
||||
|
||||
data->thread = StartThread(SolarisProc, device);
|
||||
if(data->thread == NULL)
|
||||
{
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
ATOMIC_STORE_SEQ(&self->killNow, AL_FALSE);
|
||||
if(althrd_create(&self->thread, ALCsolarisBackend_mixerProc, self) != althrd_success)
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void solaris_stop_playback(ALCdevice *device)
|
||||
static void ALCsolarisBackend_stop(ALCsolarisBackend *self)
|
||||
{
|
||||
solaris_data *data = (solaris_data*)device->ExtraData;
|
||||
int res;
|
||||
|
||||
if(!data->thread)
|
||||
if(ATOMIC_EXCHANGE_SEQ(&self->killNow, AL_TRUE))
|
||||
return;
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
data->killNow = 0;
|
||||
if(ioctl(data->fd, AUDIO_DRAIN) < 0)
|
||||
if(ioctl(self->fd, AUDIO_DRAIN) < 0)
|
||||
ERR("Error draining device: %s\n", strerror(errno));
|
||||
|
||||
free(data->mix_data);
|
||||
data->mix_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs solaris_funcs = {
|
||||
solaris_open_playback,
|
||||
solaris_close_playback,
|
||||
solaris_reset_playback,
|
||||
solaris_start_playback,
|
||||
solaris_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
typedef struct ALCsolarisBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCsolarisBackendFactory;
|
||||
#define ALCSOLARISBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsolarisBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_solaris_init(BackendFuncs *func_list)
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCsolarisBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsolarisBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCsolarisBackendFactory_getFactory(void)
|
||||
{
|
||||
ConfigValueStr("solaris", "device", &solaris_driver);
|
||||
static ALCsolarisBackendFactory factory = ALCSOLARISBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
*func_list = solaris_funcs;
|
||||
|
||||
static ALCboolean ALCsolarisBackendFactory_init(ALCsolarisBackendFactory* UNUSED(self))
|
||||
{
|
||||
ConfigValueStr(NULL, "solaris", "device", &solaris_driver);
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_solaris_deinit(void)
|
||||
static ALCboolean ALCsolarisBackendFactory_querySupport(ALCsolarisBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return ALC_TRUE;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_solaris_probe(enum DevProbe type)
|
||||
static void ALCsolarisBackendFactory_probe(ALCsolarisBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@@ -272,7 +337,7 @@ void alc_solaris_probe(enum DevProbe type)
|
||||
struct stat buf;
|
||||
if(stat(solaris_driver, &buf) == 0)
|
||||
#endif
|
||||
AppendAllDeviceList(solaris_device);
|
||||
alstr_append_range(outnames, solaris_device, solaris_device+sizeof(solaris_device));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -280,3 +345,16 @@ void alc_solaris_probe(enum DevProbe type)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ALCbackend* ALCsolarisBackendFactory_createBackend(ALCsolarisBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCsolarisBackend *backend;
|
||||
NEW_OBJ(backend, ALCsolarisBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+267
-172
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -23,21 +23,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
long DataStart;
|
||||
|
||||
ALvoid *buffer;
|
||||
ALuint size;
|
||||
|
||||
volatile int killNow;
|
||||
ALvoid *thread;
|
||||
} wave_data;
|
||||
#include "backends/base.h"
|
||||
|
||||
|
||||
static const ALCchar waveDevice[] = "Wave File Writer";
|
||||
@@ -51,101 +45,168 @@ static const ALubyte SUBTYPE_FLOAT[] = {
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
|
||||
static const ALuint channel_masks[] = {
|
||||
0, /* invalid */
|
||||
0x4, /* Mono */
|
||||
0x1 | 0x2, /* Stereo */
|
||||
0, /* 3 channel */
|
||||
0x1 | 0x2 | 0x10 | 0x20, /* Quad */
|
||||
0, /* 5 channel */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20, /* 5.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x100 | 0x200 | 0x400, /* 6.1 */
|
||||
0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x200 | 0x400, /* 7.1 */
|
||||
static const ALubyte SUBTYPE_BFORMAT_PCM[] = {
|
||||
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||
0xca, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const ALubyte SUBTYPE_BFORMAT_FLOAT[] = {
|
||||
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||
0xca, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static void fwrite16le(ALushort val, FILE *f)
|
||||
{
|
||||
fputc(val&0xff, f);
|
||||
fputc((val>>8)&0xff, f);
|
||||
ALubyte data[2] = { val&0xff, (val>>8)&0xff };
|
||||
fwrite(data, 1, 2, f);
|
||||
}
|
||||
|
||||
static void fwrite32le(ALuint val, FILE *f)
|
||||
{
|
||||
fputc(val&0xff, f);
|
||||
fputc((val>>8)&0xff, f);
|
||||
fputc((val>>16)&0xff, f);
|
||||
fputc((val>>24)&0xff, f);
|
||||
ALubyte data[4] = { val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff };
|
||||
fwrite(data, 1, 4, f);
|
||||
}
|
||||
|
||||
|
||||
static ALuint WaveProc(ALvoid *ptr)
|
||||
{
|
||||
ALCdevice *pDevice = (ALCdevice*)ptr;
|
||||
wave_data *data = (wave_data*)pDevice->ExtraData;
|
||||
ALuint frameSize;
|
||||
ALuint now, start;
|
||||
ALuint64 avail, done;
|
||||
size_t fs;
|
||||
const ALuint restTime = (ALuint64)pDevice->UpdateSize * 1000 /
|
||||
pDevice->Frequency / 2;
|
||||
typedef struct ALCwaveBackend {
|
||||
DERIVE_FROM_TYPE(ALCbackend);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
|
||||
FILE *mFile;
|
||||
long mDataStart;
|
||||
|
||||
ALvoid *mBuffer;
|
||||
ALuint mSize;
|
||||
|
||||
ATOMIC(ALenum) killNow;
|
||||
althrd_t thread;
|
||||
} ALCwaveBackend;
|
||||
|
||||
static int ALCwaveBackend_mixerProc(void *ptr);
|
||||
|
||||
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device);
|
||||
static void ALCwaveBackend_Destruct(ALCwaveBackend *self);
|
||||
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name);
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self);
|
||||
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self);
|
||||
static void ALCwaveBackend_stop(ALCwaveBackend *self);
|
||||
static DECLARE_FORWARD2(ALCwaveBackend, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ALCuint, availableSamples)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, ClockLatency, getClockLatency)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, lock)
|
||||
static DECLARE_FORWARD(ALCwaveBackend, ALCbackend, void, unlock)
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALCwaveBackend)
|
||||
|
||||
DEFINE_ALCBACKEND_VTABLE(ALCwaveBackend);
|
||||
|
||||
|
||||
static void ALCwaveBackend_Construct(ALCwaveBackend *self, ALCdevice *device)
|
||||
{
|
||||
ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
|
||||
SET_VTABLE2(ALCwaveBackend, ALCbackend, self);
|
||||
|
||||
self->mFile = NULL;
|
||||
self->mDataStart = -1;
|
||||
|
||||
self->mBuffer = NULL;
|
||||
self->mSize = 0;
|
||||
|
||||
ATOMIC_INIT(&self->killNow, AL_TRUE);
|
||||
}
|
||||
|
||||
static void ALCwaveBackend_Destruct(ALCwaveBackend *self)
|
||||
{
|
||||
if(self->mFile)
|
||||
fclose(self->mFile);
|
||||
self->mFile = NULL;
|
||||
|
||||
ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
|
||||
}
|
||||
|
||||
static int ALCwaveBackend_mixerProc(void *ptr)
|
||||
{
|
||||
ALCwaveBackend *self = (ALCwaveBackend*)ptr;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
struct timespec now, start;
|
||||
ALint64 avail, done;
|
||||
ALuint frameSize;
|
||||
size_t fs;
|
||||
const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 /
|
||||
device->Frequency / 2);
|
||||
|
||||
althrd_setname(althrd_current(), MIXER_THREAD_NAME);
|
||||
|
||||
frameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder);
|
||||
|
||||
done = 0;
|
||||
start = timeGetTime();
|
||||
while(!data->killNow && pDevice->Connected)
|
||||
if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
now = timeGetTime();
|
||||
ERR("Failed to get starting time\n");
|
||||
return 1;
|
||||
}
|
||||
while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) &&
|
||||
ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
|
||||
{
|
||||
if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC)
|
||||
{
|
||||
ERR("Failed to get current time\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
avail = (ALuint64)(now-start) * pDevice->Frequency / 1000;
|
||||
avail = (now.tv_sec - start.tv_sec) * device->Frequency;
|
||||
avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000;
|
||||
if(avail < done)
|
||||
{
|
||||
/* Timer wrapped (50 days???). Add the remainder of the cycle to
|
||||
* the available count and reset the number of samples done */
|
||||
avail += ((ALuint64)1<<32)*pDevice->Frequency/1000 - done;
|
||||
done = 0;
|
||||
}
|
||||
if(avail-done < pDevice->UpdateSize)
|
||||
{
|
||||
Sleep(restTime);
|
||||
continue;
|
||||
/* Oops, time skipped backwards. Reset the number of samples done
|
||||
* with one update available since we (likely) just came back from
|
||||
* sleeping. */
|
||||
done = avail - device->UpdateSize;
|
||||
}
|
||||
|
||||
while(avail-done >= pDevice->UpdateSize)
|
||||
if(avail-done < device->UpdateSize)
|
||||
al_nssleep(restTime);
|
||||
else while(avail-done >= device->UpdateSize)
|
||||
{
|
||||
aluMixData(pDevice, data->buffer, pDevice->UpdateSize);
|
||||
done += pDevice->UpdateSize;
|
||||
ALCwaveBackend_lock(self);
|
||||
aluMixData(device, self->mBuffer, device->UpdateSize);
|
||||
ALCwaveBackend_unlock(self);
|
||||
done += device->UpdateSize;
|
||||
|
||||
if(!IS_LITTLE_ENDIAN)
|
||||
{
|
||||
ALuint bytesize = BytesFromDevFmt(pDevice->FmtType);
|
||||
ALubyte *bytes = data->buffer;
|
||||
ALuint bytesize = BytesFromDevFmt(device->FmtType);
|
||||
ALuint i;
|
||||
|
||||
if(bytesize == 1)
|
||||
if(bytesize == 2)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i], data->f);
|
||||
}
|
||||
else if(bytesize == 2)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^1], data->f);
|
||||
ALushort *samples = self->mBuffer;
|
||||
ALuint len = self->mSize / 2;
|
||||
for(i = 0;i < len;i++)
|
||||
{
|
||||
ALushort samp = samples[i];
|
||||
samples[i] = (samp>>8) | (samp<<8);
|
||||
}
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
for(i = 0;i < data->size;i++)
|
||||
fputc(bytes[i^3], data->f);
|
||||
ALuint *samples = self->mBuffer;
|
||||
ALuint len = self->mSize / 4;
|
||||
for(i = 0;i < len;i++)
|
||||
{
|
||||
ALuint samp = samples[i];
|
||||
samples[i] = (samp>>24) | ((samp>>8)&0x0000ff00) |
|
||||
((samp<<8)&0x00ff0000) | (samp<<24);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
fs = fwrite(data->buffer, frameSize, pDevice->UpdateSize,
|
||||
data->f);
|
||||
if(ferror(data->f))
|
||||
|
||||
fs = fwrite(self->mBuffer, frameSize, device->UpdateSize, self->mFile);
|
||||
(void)fs;
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
aluHandleDisconnect(pDevice);
|
||||
ALCdevice_Lock(device);
|
||||
aluHandleDisconnect(device, "Failed to write playback samples");
|
||||
ALCdevice_Unlock(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -154,52 +215,48 @@ static ALuint WaveProc(ALvoid *ptr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ALCenum wave_open_playback(ALCdevice *device, const ALCchar *deviceName)
|
||||
|
||||
static ALCenum ALCwaveBackend_open(ALCwaveBackend *self, const ALCchar *name)
|
||||
{
|
||||
wave_data *data;
|
||||
ALCdevice *device;
|
||||
const char *fname;
|
||||
|
||||
fname = GetConfigValue("wave", "file", "");
|
||||
if(!fname[0])
|
||||
fname = GetConfigValue(NULL, "wave", "file", "");
|
||||
if(!fname[0]) return ALC_INVALID_VALUE;
|
||||
|
||||
if(!name)
|
||||
name = waveDevice;
|
||||
else if(strcmp(name, waveDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
if(!deviceName)
|
||||
deviceName = waveDevice;
|
||||
else if(strcmp(deviceName, waveDevice) != 0)
|
||||
return ALC_INVALID_VALUE;
|
||||
|
||||
data = (wave_data*)calloc(1, sizeof(wave_data));
|
||||
|
||||
data->f = fopen(fname, "wb");
|
||||
if(!data->f)
|
||||
self->mFile = al_fopen(fname, "wb");
|
||||
if(!self->mFile)
|
||||
{
|
||||
free(data);
|
||||
ERR("Could not open file '%s': %s\n", fname, strerror(errno));
|
||||
return ALC_INVALID_VALUE;
|
||||
}
|
||||
|
||||
device->szDeviceName = strdup(deviceName);
|
||||
device->ExtraData = data;
|
||||
device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
alstr_copy_cstr(&device->DeviceName, name);
|
||||
|
||||
return ALC_NO_ERROR;
|
||||
}
|
||||
|
||||
static void wave_close_playback(ALCdevice *device)
|
||||
static ALCboolean ALCwaveBackend_reset(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
|
||||
fclose(data->f);
|
||||
free(data);
|
||||
device->ExtraData = NULL;
|
||||
}
|
||||
|
||||
static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint channels=0, bits=0;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
ALuint channels=0, bits=0, chanmask=0;
|
||||
int isbformat = 0;
|
||||
size_t val;
|
||||
|
||||
fseek(data->f, 0, SEEK_SET);
|
||||
clearerr(data->f);
|
||||
fseek(self->mFile, 0, SEEK_SET);
|
||||
clearerr(self->mFile);
|
||||
|
||||
if(GetConfigValueBool(NULL, "wave", "bformat", 0))
|
||||
{
|
||||
device->FmtChans = DevFmtAmbi3D;
|
||||
device->AmbiOrder = 1;
|
||||
}
|
||||
|
||||
switch(device->FmtType)
|
||||
{
|
||||
@@ -218,141 +275,179 @@ static ALCboolean wave_reset_playback(ALCdevice *device)
|
||||
case DevFmtFloat:
|
||||
break;
|
||||
}
|
||||
switch(device->FmtChans)
|
||||
{
|
||||
case DevFmtMono: chanmask = 0x04; break;
|
||||
case DevFmtStereo: chanmask = 0x01 | 0x02; break;
|
||||
case DevFmtQuad: chanmask = 0x01 | 0x02 | 0x10 | 0x20; break;
|
||||
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
|
||||
case DevFmtX51Rear: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020; break;
|
||||
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
|
||||
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
||||
case DevFmtAmbi3D:
|
||||
/* .amb output requires FuMa */
|
||||
device->AmbiLayout = AmbiLayout_FuMa;
|
||||
device->AmbiScale = AmbiNorm_FuMa;
|
||||
isbformat = 1;
|
||||
chanmask = 0;
|
||||
break;
|
||||
}
|
||||
bits = BytesFromDevFmt(device->FmtType) * 8;
|
||||
channels = ChannelsFromDevFmt(device->FmtChans);
|
||||
channels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
|
||||
|
||||
fprintf(data->f, "RIFF");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'RIFF' header len; filled in at close
|
||||
fputs("RIFF", self->mFile);
|
||||
fwrite32le(0xFFFFFFFF, self->mFile); // 'RIFF' header len; filled in at close
|
||||
|
||||
fprintf(data->f, "WAVE");
|
||||
fputs("WAVE", self->mFile);
|
||||
|
||||
fprintf(data->f, "fmt ");
|
||||
fwrite32le(40, data->f); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
fputs("fmt ", self->mFile);
|
||||
fwrite32le(40, self->mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fwrite16le(0xFFFE, data->f);
|
||||
fwrite16le(0xFFFE, self->mFile);
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(channels, data->f);
|
||||
fwrite16le(channels, self->mFile);
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(device->Frequency, data->f);
|
||||
fwrite32le(device->Frequency, self->mFile);
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(device->Frequency * channels * bits / 8, data->f);
|
||||
fwrite32le(device->Frequency * channels * bits / 8, self->mFile);
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(channels * bits / 8, data->f);
|
||||
fwrite16le(channels * bits / 8, self->mFile);
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
fwrite16le(bits, self->mFile);
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(22, data->f);
|
||||
fwrite16le(22, self->mFile);
|
||||
// 16-bit val, valid bits per sample
|
||||
fwrite16le(bits, data->f);
|
||||
fwrite16le(bits, self->mFile);
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(channel_masks[channels], data->f);
|
||||
fwrite32le(chanmask, self->mFile);
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite(((bits==32) ? SUBTYPE_FLOAT : SUBTYPE_PCM), 1, 16, data->f);
|
||||
val = fwrite((device->FmtType == DevFmtFloat) ?
|
||||
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, self->mFile);
|
||||
(void)val;
|
||||
|
||||
fprintf(data->f, "data");
|
||||
fwrite32le(0xFFFFFFFF, data->f); // 'data' header len; filled in at close
|
||||
fputs("data", self->mFile);
|
||||
fwrite32le(0xFFFFFFFF, self->mFile); // 'data' header len; filled in at close
|
||||
|
||||
if(ferror(data->f))
|
||||
if(ferror(self->mFile))
|
||||
{
|
||||
ERR("Error writing header: %s\n", strerror(errno));
|
||||
return ALC_FALSE;
|
||||
}
|
||||
data->DataStart = ftell(data->f);
|
||||
self->mDataStart = ftell(self->mFile);
|
||||
|
||||
SetDefaultWFXChannelOrder(device);
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static ALCboolean wave_start_playback(ALCdevice *device)
|
||||
static ALCboolean ALCwaveBackend_start(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
|
||||
|
||||
data->size = device->UpdateSize * FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
|
||||
data->buffer = malloc(data->size);
|
||||
if(!data->buffer)
|
||||
self->mSize = device->UpdateSize * FrameSizeFromDevFmt(
|
||||
device->FmtChans, device->FmtType, device->AmbiOrder
|
||||
);
|
||||
self->mBuffer = malloc(self->mSize);
|
||||
if(!self->mBuffer)
|
||||
{
|
||||
ERR("Buffer malloc failed\n");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
data->thread = StartThread(WaveProc, device);
|
||||
if(data->thread == NULL)
|
||||
ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
|
||||
if(althrd_create(&self->thread, ALCwaveBackend_mixerProc, self) != althrd_success)
|
||||
{
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
free(self->mBuffer);
|
||||
self->mBuffer = NULL;
|
||||
self->mSize = 0;
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
static void wave_stop_playback(ALCdevice *device)
|
||||
static void ALCwaveBackend_stop(ALCwaveBackend *self)
|
||||
{
|
||||
wave_data *data = (wave_data*)device->ExtraData;
|
||||
ALuint dataLen;
|
||||
long size;
|
||||
int res;
|
||||
|
||||
if(!data->thread)
|
||||
if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel))
|
||||
return;
|
||||
althrd_join(self->thread, &res);
|
||||
|
||||
data->killNow = 1;
|
||||
StopThread(data->thread);
|
||||
data->thread = NULL;
|
||||
free(self->mBuffer);
|
||||
self->mBuffer = NULL;
|
||||
|
||||
data->killNow = 0;
|
||||
|
||||
free(data->buffer);
|
||||
data->buffer = NULL;
|
||||
|
||||
size = ftell(data->f);
|
||||
size = ftell(self->mFile);
|
||||
if(size > 0)
|
||||
{
|
||||
dataLen = size - data->DataStart;
|
||||
if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(dataLen, data->f); // 'data' header len
|
||||
if(fseek(data->f, 4, SEEK_SET) == 0)
|
||||
fwrite32le(size-8, data->f); // 'WAVE' header len
|
||||
dataLen = size - self->mDataStart;
|
||||
if(fseek(self->mFile, self->mDataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(dataLen, self->mFile); // 'data' header len
|
||||
if(fseek(self->mFile, 4, SEEK_SET) == 0)
|
||||
fwrite32le(size-8, self->mFile); // 'WAVE' header len
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const BackendFuncs wave_funcs = {
|
||||
wave_open_playback,
|
||||
wave_close_playback,
|
||||
wave_reset_playback,
|
||||
wave_start_playback,
|
||||
wave_stop_playback,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
typedef struct ALCwaveBackendFactory {
|
||||
DERIVE_FROM_TYPE(ALCbackendFactory);
|
||||
} ALCwaveBackendFactory;
|
||||
#define ALCWAVEBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwaveBackendFactory, ALCbackendFactory) } }
|
||||
|
||||
ALCboolean alc_wave_init(BackendFuncs *func_list)
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void);
|
||||
|
||||
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory *self);
|
||||
static DECLARE_FORWARD(ALCwaveBackendFactory, ALCbackendFactory, void, deinit)
|
||||
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory *self, ALCbackend_Type type);
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory *self, enum DevProbe type, al_string *outnames);
|
||||
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
|
||||
DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwaveBackendFactory);
|
||||
|
||||
|
||||
ALCbackendFactory *ALCwaveBackendFactory_getFactory(void)
|
||||
{
|
||||
static ALCwaveBackendFactory factory = ALCWAVEBACKENDFACTORY_INITIALIZER;
|
||||
return STATIC_CAST(ALCbackendFactory, &factory);
|
||||
}
|
||||
|
||||
|
||||
static ALCboolean ALCwaveBackendFactory_init(ALCwaveBackendFactory* UNUSED(self))
|
||||
{
|
||||
*func_list = wave_funcs;
|
||||
return ALC_TRUE;
|
||||
}
|
||||
|
||||
void alc_wave_deinit(void)
|
||||
static ALCboolean ALCwaveBackendFactory_querySupport(ALCwaveBackendFactory* UNUSED(self), ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
return !!ConfigValueExists(NULL, "wave", "file");
|
||||
return ALC_FALSE;
|
||||
}
|
||||
|
||||
void alc_wave_probe(enum DevProbe type)
|
||||
static void ALCwaveBackendFactory_probe(ALCwaveBackendFactory* UNUSED(self), enum DevProbe type, al_string *outnames)
|
||||
{
|
||||
if(!ConfigValueExists("wave", "file"))
|
||||
return;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case ALL_DEVICE_PROBE:
|
||||
AppendAllDeviceList(waveDevice);
|
||||
alstr_append_range(outnames, waveDevice, waveDevice+sizeof(waveDevice));
|
||||
break;
|
||||
case CAPTURE_DEVICE_PROBE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ALCbackend* ALCwaveBackendFactory_createBackend(ALCwaveBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
|
||||
{
|
||||
if(type == ALCbackend_Playback)
|
||||
{
|
||||
ALCwaveBackend *backend;
|
||||
NEW_OBJ(backend, ALCwaveBackend)(device);
|
||||
if(!backend) return NULL;
|
||||
return STATIC_CAST(ALCbackend, backend);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+499
-490
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,492 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "bformatdec.h"
|
||||
#include "ambdec.h"
|
||||
#include "filters/splitter.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "bool.h"
|
||||
#include "threads.h"
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
|
||||
* coefficients should be divided by these values to get proper N3D scalings.
|
||||
*/
|
||||
const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.000000000f, /* ACN 0 (W), sqrt(1) */
|
||||
1.732050808f, /* ACN 1 (Y), sqrt(3) */
|
||||
1.732050808f, /* ACN 2 (Z), sqrt(3) */
|
||||
1.732050808f, /* ACN 3 (X), sqrt(3) */
|
||||
2.236067978f, /* ACN 4 (V), sqrt(5) */
|
||||
2.236067978f, /* ACN 5 (T), sqrt(5) */
|
||||
2.236067978f, /* ACN 6 (R), sqrt(5) */
|
||||
2.236067978f, /* ACN 7 (S), sqrt(5) */
|
||||
2.236067978f, /* ACN 8 (U), sqrt(5) */
|
||||
2.645751311f, /* ACN 9 (Q), sqrt(7) */
|
||||
2.645751311f, /* ACN 10 (O), sqrt(7) */
|
||||
2.645751311f, /* ACN 11 (M), sqrt(7) */
|
||||
2.645751311f, /* ACN 12 (K), sqrt(7) */
|
||||
2.645751311f, /* ACN 13 (L), sqrt(7) */
|
||||
2.645751311f, /* ACN 14 (N), sqrt(7) */
|
||||
2.645751311f, /* ACN 15 (P), sqrt(7) */
|
||||
};
|
||||
const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS] = {
|
||||
1.414213562f, /* ACN 0 (W), sqrt(2) */
|
||||
1.732050808f, /* ACN 1 (Y), sqrt(3) */
|
||||
1.732050808f, /* ACN 2 (Z), sqrt(3) */
|
||||
1.732050808f, /* ACN 3 (X), sqrt(3) */
|
||||
1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
|
||||
1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
|
||||
2.236067978f, /* ACN 6 (R), sqrt(5) */
|
||||
1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
|
||||
1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
|
||||
2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
|
||||
1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
|
||||
2.231093404f, /* ACN 11 (M), sqrt(224/45) */
|
||||
2.645751311f, /* ACN 12 (K), sqrt(7) */
|
||||
2.231093404f, /* ACN 13 (L), sqrt(224/45) */
|
||||
1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
|
||||
2.091650066f, /* ACN 15 (P), sqrt(35/8) */
|
||||
};
|
||||
|
||||
|
||||
#define HF_BAND 0
|
||||
#define LF_BAND 1
|
||||
#define NUM_BANDS 2
|
||||
|
||||
/* These points are in AL coordinates! */
|
||||
static const ALfloat Ambi3DPoints[8][3] = {
|
||||
{ -0.577350269f, 0.577350269f, -0.577350269f },
|
||||
{ 0.577350269f, 0.577350269f, -0.577350269f },
|
||||
{ -0.577350269f, 0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, 0.577350269f, 0.577350269f },
|
||||
{ -0.577350269f, -0.577350269f, -0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, -0.577350269f },
|
||||
{ -0.577350269f, -0.577350269f, 0.577350269f },
|
||||
{ 0.577350269f, -0.577350269f, 0.577350269f },
|
||||
};
|
||||
static const ALfloat Ambi3DDecoder[8][MAX_AMBI_COEFFS] = {
|
||||
{ 0.125f, 0.125f, 0.125f, 0.125f },
|
||||
{ 0.125f, -0.125f, 0.125f, 0.125f },
|
||||
{ 0.125f, 0.125f, 0.125f, -0.125f },
|
||||
{ 0.125f, -0.125f, 0.125f, -0.125f },
|
||||
{ 0.125f, 0.125f, -0.125f, 0.125f },
|
||||
{ 0.125f, -0.125f, -0.125f, 0.125f },
|
||||
{ 0.125f, 0.125f, -0.125f, -0.125f },
|
||||
{ 0.125f, -0.125f, -0.125f, -0.125f },
|
||||
};
|
||||
static const ALfloat Ambi3DDecoderHFScale[MAX_AMBI_COEFFS] = {
|
||||
2.0f,
|
||||
1.15470054f, 1.15470054f, 1.15470054f
|
||||
};
|
||||
|
||||
|
||||
/* NOTE: BandSplitter filters are unused with single-band decoding */
|
||||
typedef struct BFormatDec {
|
||||
ALuint Enabled; /* Bitfield of enabled channels. */
|
||||
|
||||
union {
|
||||
alignas(16) ALfloat Dual[MAX_OUTPUT_CHANNELS][NUM_BANDS][MAX_AMBI_COEFFS];
|
||||
alignas(16) ALfloat Single[MAX_OUTPUT_CHANNELS][MAX_AMBI_COEFFS];
|
||||
} Matrix;
|
||||
|
||||
BandSplitter XOver[MAX_AMBI_COEFFS];
|
||||
|
||||
ALfloat (*Samples)[BUFFERSIZE];
|
||||
/* These two alias into Samples */
|
||||
ALfloat (*SamplesHF)[BUFFERSIZE];
|
||||
ALfloat (*SamplesLF)[BUFFERSIZE];
|
||||
|
||||
alignas(16) ALfloat ChannelMix[BUFFERSIZE];
|
||||
|
||||
struct {
|
||||
BandSplitter XOver;
|
||||
ALfloat Gains[NUM_BANDS];
|
||||
} UpSampler[4];
|
||||
|
||||
ALsizei NumChannels;
|
||||
ALboolean DualBand;
|
||||
} BFormatDec;
|
||||
|
||||
BFormatDec *bformatdec_alloc()
|
||||
{
|
||||
return al_calloc(16, sizeof(BFormatDec));
|
||||
}
|
||||
|
||||
void bformatdec_free(BFormatDec **dec)
|
||||
{
|
||||
if(dec && *dec)
|
||||
{
|
||||
al_free((*dec)->Samples);
|
||||
(*dec)->Samples = NULL;
|
||||
(*dec)->SamplesHF = NULL;
|
||||
(*dec)->SamplesLF = NULL;
|
||||
|
||||
al_free(*dec);
|
||||
*dec = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void bformatdec_reset(BFormatDec *dec, const AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS])
|
||||
{
|
||||
static const ALsizei map2DTo3D[MAX_AMBI2D_COEFFS] = {
|
||||
0, 1, 3, 4, 8, 9, 15
|
||||
};
|
||||
const ALfloat *coeff_scale = N3D2N3DScale;
|
||||
bool periphonic;
|
||||
ALfloat ratio;
|
||||
ALsizei i;
|
||||
|
||||
al_free(dec->Samples);
|
||||
dec->Samples = NULL;
|
||||
dec->SamplesHF = NULL;
|
||||
dec->SamplesLF = NULL;
|
||||
|
||||
dec->NumChannels = chancount;
|
||||
dec->Samples = al_calloc(16, dec->NumChannels*2 * sizeof(dec->Samples[0]));
|
||||
dec->SamplesHF = dec->Samples;
|
||||
dec->SamplesLF = dec->SamplesHF + dec->NumChannels;
|
||||
|
||||
dec->Enabled = 0;
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
dec->Enabled |= 1 << chanmap[i];
|
||||
|
||||
if(conf->CoeffScale == ADS_SN3D)
|
||||
coeff_scale = SN3D2N3DScale;
|
||||
else if(conf->CoeffScale == ADS_FuMa)
|
||||
coeff_scale = FuMa2N3DScale;
|
||||
|
||||
memset(dec->UpSampler, 0, sizeof(dec->UpSampler));
|
||||
ratio = 400.0f / (ALfloat)srate;
|
||||
for(i = 0;i < 4;i++)
|
||||
bandsplit_init(&dec->UpSampler[i].XOver, ratio);
|
||||
if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
|
||||
{
|
||||
periphonic = true;
|
||||
|
||||
dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H3P :
|
||||
(conf->ChanMask > 0xf) ? W_SCALE_2H2P : 1.0f;
|
||||
dec->UpSampler[0].Gains[LF_BAND] = 1.0f;
|
||||
for(i = 1;i < 4;i++)
|
||||
{
|
||||
dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H3P :
|
||||
(conf->ChanMask > 0xf) ? XYZ_SCALE_2H2P : 1.0f;
|
||||
dec->UpSampler[i].Gains[LF_BAND] = 1.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
periphonic = false;
|
||||
|
||||
dec->UpSampler[0].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? W_SCALE_3H0P :
|
||||
(conf->ChanMask > 0xf) ? W_SCALE_2H0P : 1.0f;
|
||||
dec->UpSampler[0].Gains[LF_BAND] = 1.0f;
|
||||
for(i = 1;i < 3;i++)
|
||||
{
|
||||
dec->UpSampler[i].Gains[HF_BAND] = (conf->ChanMask > 0x1ff) ? XYZ_SCALE_3H0P :
|
||||
(conf->ChanMask > 0xf) ? XYZ_SCALE_2H0P : 1.0f;
|
||||
dec->UpSampler[i].Gains[LF_BAND] = 1.0f;
|
||||
}
|
||||
dec->UpSampler[3].Gains[HF_BAND] = 0.0f;
|
||||
dec->UpSampler[3].Gains[LF_BAND] = 0.0f;
|
||||
}
|
||||
|
||||
memset(&dec->Matrix, 0, sizeof(dec->Matrix));
|
||||
if(conf->FreqBands == 1)
|
||||
{
|
||||
dec->DualBand = AL_FALSE;
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
{
|
||||
ALsizei chan = chanmap[i];
|
||||
ALfloat gain;
|
||||
ALsizei j, k;
|
||||
|
||||
if(!periphonic)
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
||||
{
|
||||
ALsizei l = map2DTo3D[j];
|
||||
if(j == 0) gain = conf->HFOrderGain[0];
|
||||
else if(j == 1) gain = conf->HFOrderGain[1];
|
||||
else if(j == 3) gain = conf->HFOrderGain[2];
|
||||
else if(j == 5) gain = conf->HFOrderGain[3];
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[l] *
|
||||
gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
{
|
||||
if(j == 0) gain = conf->HFOrderGain[0];
|
||||
else if(j == 1) gain = conf->HFOrderGain[1];
|
||||
else if(j == 4) gain = conf->HFOrderGain[2];
|
||||
else if(j == 9) gain = conf->HFOrderGain[3];
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Single[chan][j] = conf->HFMatrix[i][k++] / coeff_scale[j] *
|
||||
gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dec->DualBand = AL_TRUE;
|
||||
|
||||
ratio = conf->XOverFreq / (ALfloat)srate;
|
||||
for(i = 0;i < MAX_AMBI_COEFFS;i++)
|
||||
bandsplit_init(&dec->XOver[i], ratio);
|
||||
|
||||
ratio = powf(10.0f, conf->XOverRatio / 40.0f);
|
||||
for(i = 0;i < conf->NumSpeakers;i++)
|
||||
{
|
||||
ALsizei chan = chanmap[i];
|
||||
ALfloat gain;
|
||||
ALsizei j, k;
|
||||
|
||||
if(!periphonic)
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
||||
{
|
||||
ALsizei l = map2DTo3D[j];
|
||||
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
|
||||
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
|
||||
else if(j == 3) gain = conf->HFOrderGain[2] * ratio;
|
||||
else if(j == 5) gain = conf->HFOrderGain[3] * ratio;
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Dual[chan][HF_BAND][j] = conf->HFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
}
|
||||
for(j = 0,k = 0;j < MAX_AMBI2D_COEFFS;j++)
|
||||
{
|
||||
ALsizei l = map2DTo3D[j];
|
||||
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
|
||||
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
|
||||
else if(j == 3) gain = conf->LFOrderGain[2] / ratio;
|
||||
else if(j == 5) gain = conf->LFOrderGain[3] / ratio;
|
||||
if((conf->ChanMask&(1<<l)))
|
||||
dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[l] * gain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
{
|
||||
if(j == 0) gain = conf->HFOrderGain[0] * ratio;
|
||||
else if(j == 1) gain = conf->HFOrderGain[1] * ratio;
|
||||
else if(j == 4) gain = conf->HFOrderGain[2] * ratio;
|
||||
else if(j == 9) gain = conf->HFOrderGain[3] * ratio;
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Dual[chan][HF_BAND][j] = conf->HFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
}
|
||||
for(j = 0,k = 0;j < MAX_AMBI_COEFFS;j++)
|
||||
{
|
||||
if(j == 0) gain = conf->LFOrderGain[0] / ratio;
|
||||
else if(j == 1) gain = conf->LFOrderGain[1] / ratio;
|
||||
else if(j == 4) gain = conf->LFOrderGain[2] / ratio;
|
||||
else if(j == 9) gain = conf->LFOrderGain[3] / ratio;
|
||||
if((conf->ChanMask&(1<<j)))
|
||||
dec->Matrix.Dual[chan][LF_BAND][j] = conf->LFMatrix[i][k++] /
|
||||
coeff_scale[j] * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
|
||||
{
|
||||
ALsizei chan, i;
|
||||
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
if(dec->DualBand)
|
||||
{
|
||||
for(i = 0;i < dec->NumChannels;i++)
|
||||
bandsplit_process(&dec->XOver[i], dec->SamplesHF[i], dec->SamplesLF[i],
|
||||
InSamples[i], SamplesToDo);
|
||||
|
||||
for(chan = 0;chan < OutChannels;chan++)
|
||||
{
|
||||
if(!(dec->Enabled&(1<<chan)))
|
||||
continue;
|
||||
|
||||
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][HF_BAND],
|
||||
dec->SamplesHF, dec->NumChannels, 0, SamplesToDo
|
||||
);
|
||||
MixRowSamples(dec->ChannelMix, dec->Matrix.Dual[chan][LF_BAND],
|
||||
dec->SamplesLF, dec->NumChannels, 0, SamplesToDo
|
||||
);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[chan][i] += dec->ChannelMix[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(chan = 0;chan < OutChannels;chan++)
|
||||
{
|
||||
if(!(dec->Enabled&(1<<chan)))
|
||||
continue;
|
||||
|
||||
memset(dec->ChannelMix, 0, SamplesToDo*sizeof(ALfloat));
|
||||
MixRowSamples(dec->ChannelMix, dec->Matrix.Single[chan], InSamples,
|
||||
dec->NumChannels, 0, SamplesToDo);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[chan][i] += dec->ChannelMix[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
/* This up-sampler leverages the differences observed in dual-band second-
|
||||
* and third-order decoder matrices compared to first-order. For the same
|
||||
* output channel configuration, the low-frequency matrix has identical
|
||||
* coefficients in the shared input channels, while the high-frequency
|
||||
* matrix has extra scalars applied to the W channel and X/Y/Z channels.
|
||||
* Mixing the first-order content into the higher-order stream with the
|
||||
* appropriate counter-scales applied to the HF response results in the
|
||||
* subsequent higher-order decode generating the same response as a first-
|
||||
* order decode.
|
||||
*/
|
||||
for(i = 0;i < InChannels;i++)
|
||||
{
|
||||
/* First, split the first-order components into low and high frequency
|
||||
* bands.
|
||||
*/
|
||||
bandsplit_process(&dec->UpSampler[i].XOver,
|
||||
dec->Samples[HF_BAND], dec->Samples[LF_BAND],
|
||||
InSamples[i], SamplesToDo
|
||||
);
|
||||
|
||||
/* Now write each band to the output. */
|
||||
MixRowSamples(OutBuffer[i], dec->UpSampler[i].Gains,
|
||||
dec->Samples, NUM_BANDS, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define INVALID_UPSAMPLE_INDEX INT_MAX
|
||||
|
||||
static ALsizei GetACNIndex(const BFChannelConfig *chans, ALsizei numchans, ALsizei acn)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < numchans;i++)
|
||||
{
|
||||
if(chans[i].Index == acn)
|
||||
return i;
|
||||
}
|
||||
return INVALID_UPSAMPLE_INDEX;
|
||||
}
|
||||
#define GetChannelForACN(b, a) GetACNIndex((b).Ambi.Map, (b).NumChannels, (a))
|
||||
|
||||
typedef struct AmbiUpsampler {
|
||||
alignas(16) ALfloat Samples[NUM_BANDS][BUFFERSIZE];
|
||||
|
||||
BandSplitter XOver[4];
|
||||
|
||||
ALfloat Gains[4][MAX_OUTPUT_CHANNELS][NUM_BANDS];
|
||||
} AmbiUpsampler;
|
||||
|
||||
AmbiUpsampler *ambiup_alloc()
|
||||
{
|
||||
return al_calloc(16, sizeof(AmbiUpsampler));
|
||||
}
|
||||
|
||||
void ambiup_free(struct AmbiUpsampler **ambiup)
|
||||
{
|
||||
if(ambiup)
|
||||
{
|
||||
al_free(*ambiup);
|
||||
*ambiup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale)
|
||||
{
|
||||
ALfloat ratio;
|
||||
ALsizei i;
|
||||
|
||||
ratio = 400.0f / (ALfloat)device->Frequency;
|
||||
for(i = 0;i < 4;i++)
|
||||
bandsplit_init(&ambiup->XOver[i], ratio);
|
||||
|
||||
memset(ambiup->Gains, 0, sizeof(ambiup->Gains));
|
||||
if(device->Dry.CoeffCount > 0)
|
||||
{
|
||||
ALfloat encgains[8][MAX_OUTPUT_CHANNELS];
|
||||
ALsizei j;
|
||||
size_t k;
|
||||
|
||||
for(k = 0;k < COUNTOF(Ambi3DPoints);k++)
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS] = { 0.0f };
|
||||
CalcDirectionCoeffs(Ambi3DPoints[k], 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, 1.0f, encgains[k]);
|
||||
}
|
||||
|
||||
/* Combine the matrices that do the in->virt and virt->out conversions
|
||||
* so we get a single in->out conversion. NOTE: the Encoder matrix
|
||||
* (encgains) and output are transposed, so the input channels line up
|
||||
* with the rows and the output channels line up with the columns.
|
||||
*/
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
for(j = 0;j < device->Dry.NumChannels;j++)
|
||||
{
|
||||
ALdouble gain = 0.0;
|
||||
for(k = 0;k < COUNTOF(Ambi3DDecoder);k++)
|
||||
gain += (ALdouble)Ambi3DDecoder[k][i] * encgains[k][j];
|
||||
ambiup->Gains[i][j][HF_BAND] = (ALfloat)(gain * Ambi3DDecoderHFScale[i]);
|
||||
ambiup->Gains[i][j][LF_BAND] = (ALfloat)gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
ALsizei index = GetChannelForACN(device->Dry, i);
|
||||
if(index != INVALID_UPSAMPLE_INDEX)
|
||||
{
|
||||
ALfloat scale = device->Dry.Ambi.Map[index].Scale;
|
||||
ambiup->Gains[i][index][HF_BAND] = scale * ((i==0) ? w_scale : xyz_scale);
|
||||
ambiup->Gains[i][index][LF_BAND] = scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0;i < 4;i++)
|
||||
{
|
||||
bandsplit_process(&ambiup->XOver[i],
|
||||
ambiup->Samples[HF_BAND], ambiup->Samples[LF_BAND],
|
||||
InSamples[i], SamplesToDo
|
||||
);
|
||||
|
||||
for(j = 0;j < OutChannels;j++)
|
||||
MixRowSamples(OutBuffer[j], ambiup->Gains[i][j],
|
||||
ambiup->Samples, NUM_BANDS, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef BFORMATDEC_H
|
||||
#define BFORMATDEC_H
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
|
||||
/* These are the necessary scales for first-order HF responses to play over
|
||||
* higher-order 2D (non-periphonic) decoders.
|
||||
*/
|
||||
#define W_SCALE_2H0P 1.224744871f /* sqrt(1.5) */
|
||||
#define XYZ_SCALE_2H0P 1.0f
|
||||
#define W_SCALE_3H0P 1.414213562f /* sqrt(2) */
|
||||
#define XYZ_SCALE_3H0P 1.082392196f
|
||||
|
||||
/* These are the necessary scales for first-order HF responses to play over
|
||||
* higher-order 3D (periphonic) decoders.
|
||||
*/
|
||||
#define W_SCALE_2H2P 1.341640787f /* sqrt(1.8) */
|
||||
#define XYZ_SCALE_2H2P 1.0f
|
||||
#define W_SCALE_3H3P 1.695486018f
|
||||
#define XYZ_SCALE_3H3P 1.136697713f
|
||||
|
||||
|
||||
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
|
||||
* coefficients should be divided by these values to get proper N3D scalings.
|
||||
*/
|
||||
const ALfloat N3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
const ALfloat SN3D2N3DScale[MAX_AMBI_COEFFS];
|
||||
const ALfloat FuMa2N3DScale[MAX_AMBI_COEFFS];
|
||||
|
||||
|
||||
struct AmbDecConf;
|
||||
struct BFormatDec;
|
||||
struct AmbiUpsampler;
|
||||
|
||||
|
||||
struct BFormatDec *bformatdec_alloc();
|
||||
void bformatdec_free(struct BFormatDec **dec);
|
||||
void bformatdec_reset(struct BFormatDec *dec, const struct AmbDecConf *conf, ALsizei chancount, ALuint srate, const ALsizei chanmap[MAX_OUTPUT_CHANNELS]);
|
||||
|
||||
/* Decodes the ambisonic input to the given output channels. */
|
||||
void bformatdec_process(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
|
||||
|
||||
/* Up-samples a first-order input to the decoder's configuration. */
|
||||
void bformatdec_upSample(struct BFormatDec *dec, ALfloat (*restrict OutBuffer)[BUFFERSIZE], const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei InChannels, ALsizei SamplesToDo);
|
||||
|
||||
|
||||
/* Stand-alone first-order upsampler. Kept here because it shares some stuff
|
||||
* with bformatdec. Assumes a periphonic (4-channel) input mix!
|
||||
*/
|
||||
struct AmbiUpsampler *ambiup_alloc();
|
||||
void ambiup_free(struct AmbiUpsampler **ambiup);
|
||||
void ambiup_reset(struct AmbiUpsampler *ambiup, const ALCdevice *device, ALfloat w_scale, ALfloat xyz_scale);
|
||||
|
||||
void ambiup_process(struct AmbiUpsampler *ambiup, ALfloat (*restrict OutBuffer)[BUFFERSIZE], ALsizei OutChannels, const ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
|
||||
|
||||
#endif /* BFORMATDEC_H */
|
||||
+91
-89
@@ -27,120 +27,98 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bs2b.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
/* Single pole IIR filter.
|
||||
* O[n] = a0*I[n] + a1*I[n-1] + b1*O[n-1]
|
||||
*/
|
||||
|
||||
/* Lowpass filter */
|
||||
#define lo_filter(in, out_1) (bs2b->a0_lo*(in) + bs2b->b1_lo*(out_1))
|
||||
|
||||
/* Highboost filter */
|
||||
#define hi_filter(in, in_1, out_1) (bs2b->a0_hi*(in) + bs2b->a1_hi*(in_1) + bs2b->b1_hi*(out_1))
|
||||
|
||||
/* Set up all data. */
|
||||
static void init(struct bs2b *bs2b)
|
||||
{
|
||||
double Fc_lo, Fc_hi;
|
||||
double G_lo, G_hi;
|
||||
double x;
|
||||
|
||||
if ((bs2b->srate > 192000) || (bs2b->srate < 2000))
|
||||
bs2b->srate = BS2B_DEFAULT_SRATE;
|
||||
float Fc_lo, Fc_hi;
|
||||
float G_lo, G_hi;
|
||||
float x, g;
|
||||
|
||||
switch(bs2b->level)
|
||||
{
|
||||
case BS2B_LOW_CLEVEL: /* Low crossfeed level */
|
||||
Fc_lo = 360.0;
|
||||
Fc_hi = 501.0;
|
||||
G_lo = 0.398107170553497;
|
||||
G_hi = 0.205671765275719;
|
||||
Fc_lo = 360.0f;
|
||||
Fc_hi = 501.0f;
|
||||
G_lo = 0.398107170553497f;
|
||||
G_hi = 0.205671765275719f;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */
|
||||
Fc_lo = 500.0;
|
||||
Fc_hi = 711.0;
|
||||
G_lo = 0.459726988530872;
|
||||
G_hi = 0.228208484414988;
|
||||
Fc_lo = 500.0f;
|
||||
Fc_hi = 711.0f;
|
||||
G_lo = 0.459726988530872f;
|
||||
G_hi = 0.228208484414988f;
|
||||
break;
|
||||
|
||||
case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */
|
||||
Fc_lo = 700.0;
|
||||
Fc_hi = 1021.0;
|
||||
G_lo = 0.530884444230988;
|
||||
G_hi = 0.250105790667544;
|
||||
Fc_lo = 700.0f;
|
||||
Fc_hi = 1021.0f;
|
||||
G_lo = 0.530884444230988f;
|
||||
G_hi = 0.250105790667544f;
|
||||
break;
|
||||
|
||||
case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */
|
||||
Fc_lo = 360.0;
|
||||
Fc_hi = 494.0;
|
||||
G_lo = 0.316227766016838;
|
||||
G_hi = 0.168236228897329;
|
||||
Fc_lo = 360.0f;
|
||||
Fc_hi = 494.0f;
|
||||
G_lo = 0.316227766016838f;
|
||||
G_hi = 0.168236228897329f;
|
||||
break;
|
||||
|
||||
case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */
|
||||
Fc_lo = 500.0;
|
||||
Fc_hi = 689.0;
|
||||
G_lo = 0.354813389233575;
|
||||
G_hi = 0.187169483835901;
|
||||
Fc_lo = 500.0f;
|
||||
Fc_hi = 689.0f;
|
||||
G_lo = 0.354813389233575f;
|
||||
G_hi = 0.187169483835901f;
|
||||
break;
|
||||
|
||||
default: /* High easy crossfeed level */
|
||||
bs2b->level = BS2B_HIGH_ECLEVEL;
|
||||
|
||||
Fc_lo = 700.0;
|
||||
Fc_hi = 975.0;
|
||||
G_lo = 0.398107170553497;
|
||||
G_hi = 0.205671765275719;
|
||||
Fc_lo = 700.0f;
|
||||
Fc_hi = 975.0f;
|
||||
G_lo = 0.398107170553497f;
|
||||
G_hi = 0.205671765275719f;
|
||||
break;
|
||||
} /* switch */
|
||||
|
||||
g = 1.0f / (1.0f - G_hi + G_lo);
|
||||
|
||||
/* $fc = $Fc / $s;
|
||||
* $d = 1 / 2 / pi / $fc;
|
||||
* $x = exp(-1 / $d);
|
||||
*/
|
||||
|
||||
x = exp(-2.0 * M_PI * Fc_lo / bs2b->srate);
|
||||
x = expf(-2.0f * F_PI * Fc_lo / bs2b->srate);
|
||||
bs2b->b1_lo = x;
|
||||
bs2b->a0_lo = G_lo * (1.0 - x);
|
||||
bs2b->a0_lo = G_lo * (1.0f - x) * g;
|
||||
|
||||
x = exp(-2.0 * M_PI * Fc_hi / bs2b->srate);
|
||||
x = expf(-2.0f * F_PI * Fc_hi / bs2b->srate);
|
||||
bs2b->b1_hi = x;
|
||||
bs2b->a0_hi = 1.0 - G_hi * (1.0 - x);
|
||||
bs2b->a1_hi = -x;
|
||||
|
||||
bs2b->gain = 1.0f / (float)(1.0 - G_hi + G_lo);
|
||||
bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g;
|
||||
bs2b->a1_hi = -x * g;
|
||||
} /* init */
|
||||
|
||||
|
||||
/* Exported functions.
|
||||
* See descriptions in "bs2b.h"
|
||||
*/
|
||||
|
||||
void bs2b_set_level(struct bs2b *bs2b, int level)
|
||||
void bs2b_set_params(struct bs2b *bs2b, int level, int srate)
|
||||
{
|
||||
if(level == bs2b->level)
|
||||
return;
|
||||
if(srate <= 0) srate = 1;
|
||||
|
||||
bs2b->level = level;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_level */
|
||||
} /* bs2b_set_params */
|
||||
|
||||
int bs2b_get_level(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->level;
|
||||
} /* bs2b_get_level */
|
||||
|
||||
void bs2b_set_srate(struct bs2b *bs2b, int srate)
|
||||
{
|
||||
if (srate == bs2b->srate)
|
||||
return;
|
||||
bs2b->srate = srate;
|
||||
init(bs2b);
|
||||
} /* bs2b_set_srate */
|
||||
|
||||
int bs2b_get_srate(struct bs2b *bs2b)
|
||||
{
|
||||
return bs2b->srate;
|
||||
@@ -151,35 +129,59 @@ void bs2b_clear(struct bs2b *bs2b)
|
||||
memset(&bs2b->last_sample, 0, sizeof(bs2b->last_sample));
|
||||
} /* bs2b_clear */
|
||||
|
||||
void bs2b_cross_feed(struct bs2b *bs2b, float *sample)
|
||||
void bs2b_cross_feed(struct bs2b *bs2b, float *restrict Left, float *restrict Right, int SamplesToDo)
|
||||
{
|
||||
/* Lowpass filter */
|
||||
bs2b->last_sample.lo[0] = lo_filter(sample[0], bs2b->last_sample.lo[0]);
|
||||
bs2b->last_sample.lo[1] = lo_filter(sample[1], bs2b->last_sample.lo[1]);
|
||||
float lsamples[128][2];
|
||||
float rsamples[128][2];
|
||||
int base;
|
||||
|
||||
/* Highboost filter */
|
||||
bs2b->last_sample.hi[0] = hi_filter(sample[0], bs2b->last_sample.asis[0], bs2b->last_sample.hi[0]);
|
||||
bs2b->last_sample.hi[1] = hi_filter(sample[1], bs2b->last_sample.asis[1], bs2b->last_sample.hi[1]);
|
||||
bs2b->last_sample.asis[0] = sample[0];
|
||||
bs2b->last_sample.asis[1] = sample[1];
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
int todo = mini(128, SamplesToDo-base);
|
||||
int i;
|
||||
|
||||
/* Crossfeed */
|
||||
sample[0] = (float)(bs2b->last_sample.hi[0] + bs2b->last_sample.lo[1]);
|
||||
sample[1] = (float)(bs2b->last_sample.hi[1] + bs2b->last_sample.lo[0]);
|
||||
/* Process left input */
|
||||
lsamples[0][0] = bs2b->a0_lo*Left[0] +
|
||||
bs2b->b1_lo*bs2b->last_sample[0].lo;
|
||||
lsamples[0][1] = bs2b->a0_hi*Left[0] +
|
||||
bs2b->a1_hi*bs2b->last_sample[0].asis +
|
||||
bs2b->b1_hi*bs2b->last_sample[0].hi;
|
||||
for(i = 1;i < todo;i++)
|
||||
{
|
||||
lsamples[i][0] = bs2b->a0_lo*Left[i] +
|
||||
bs2b->b1_lo*lsamples[i-1][0];
|
||||
lsamples[i][1] = bs2b->a0_hi*Left[i] +
|
||||
bs2b->a1_hi*Left[i-1] +
|
||||
bs2b->b1_hi*lsamples[i-1][1];
|
||||
}
|
||||
bs2b->last_sample[0].asis = Left[i-1];
|
||||
bs2b->last_sample[0].lo = lsamples[i-1][0];
|
||||
bs2b->last_sample[0].hi = lsamples[i-1][1];
|
||||
|
||||
/* Bass boost cause allpass attenuation */
|
||||
sample[0] *= bs2b->gain;
|
||||
sample[1] *= bs2b->gain;
|
||||
/* Process right input */
|
||||
rsamples[0][0] = bs2b->a0_lo*Right[0] +
|
||||
bs2b->b1_lo*bs2b->last_sample[1].lo;
|
||||
rsamples[0][1] = bs2b->a0_hi*Right[0] +
|
||||
bs2b->a1_hi*bs2b->last_sample[1].asis +
|
||||
bs2b->b1_hi*bs2b->last_sample[1].hi;
|
||||
for(i = 1;i < todo;i++)
|
||||
{
|
||||
rsamples[i][0] = bs2b->a0_lo*Right[i] +
|
||||
bs2b->b1_lo*rsamples[i-1][0];
|
||||
rsamples[i][1] = bs2b->a0_hi*Right[i] +
|
||||
bs2b->a1_hi*Right[i-1] +
|
||||
bs2b->b1_hi*rsamples[i-1][1];
|
||||
}
|
||||
bs2b->last_sample[1].asis = Right[i-1];
|
||||
bs2b->last_sample[1].lo = rsamples[i-1][0];
|
||||
bs2b->last_sample[1].hi = rsamples[i-1][1];
|
||||
|
||||
/* Clipping of overloaded samples */
|
||||
#if 0
|
||||
if (sample[0] > 1.0)
|
||||
sample[0] = 1.0;
|
||||
if (sample[0] < -1.0)
|
||||
sample[0] = -1.0;
|
||||
if (sample[1] > 1.0)
|
||||
sample[1] = 1.0;
|
||||
if (sample[1] < -1.0)
|
||||
sample[1] = -1.0;
|
||||
#endif
|
||||
/* Crossfeed */
|
||||
for(i = 0;i < todo;i++)
|
||||
*(Left++) = lsamples[i][1] + rsamples[i][0];
|
||||
for(i = 0;i < todo;i++)
|
||||
*(Right++) = rsamples[i][1] + lsamples[i][0];
|
||||
|
||||
base += todo;
|
||||
}
|
||||
} /* bs2b_cross_feed */
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef AL_COMPAT_H
|
||||
#define AL_COMPAT_H
|
||||
|
||||
#include "alstring.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
WCHAR *strdupW(const WCHAR *str);
|
||||
|
||||
/* Opens a file with standard I/O. The filename is expected to be UTF-8. */
|
||||
FILE *al_fopen(const char *fname, const char *mode);
|
||||
|
||||
#define HAVE_DYNLOAD 1
|
||||
|
||||
#else
|
||||
|
||||
#define al_fopen fopen
|
||||
|
||||
#if defined(HAVE_DLFCN_H) && !defined(IN_IDE_PARSER)
|
||||
#define HAVE_DYNLOAD 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
struct FileMapping {
|
||||
#ifdef _WIN32
|
||||
HANDLE file;
|
||||
HANDLE fmap;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
void *ptr;
|
||||
size_t len;
|
||||
};
|
||||
struct FileMapping MapFileToMem(const char *fname);
|
||||
void UnmapFileMem(const struct FileMapping *mapping);
|
||||
|
||||
void GetProcBinary(al_string *path, al_string *fname);
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
void *LoadLib(const char *name);
|
||||
void CloseLib(void *handle);
|
||||
void *GetSymbol(void *handle, const char *name);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AL_COMPAT_H */
|
||||
+468
@@ -0,0 +1,468 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "converter.h"
|
||||
|
||||
#include "fpu_modes.h"
|
||||
#include "mixer/defs.h"
|
||||
|
||||
|
||||
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate)
|
||||
{
|
||||
SampleConverter *converter;
|
||||
ALsizei step;
|
||||
|
||||
if(numchans <= 0 || srcRate <= 0 || dstRate <= 0)
|
||||
return NULL;
|
||||
|
||||
converter = al_calloc(16, FAM_SIZE(SampleConverter, Chan, numchans));
|
||||
converter->mSrcType = srcType;
|
||||
converter->mDstType = dstType;
|
||||
converter->mNumChannels = numchans;
|
||||
converter->mSrcTypeSize = BytesFromDevFmt(srcType);
|
||||
converter->mDstTypeSize = BytesFromDevFmt(dstType);
|
||||
|
||||
converter->mSrcPrepCount = 0;
|
||||
converter->mFracOffset = 0;
|
||||
|
||||
/* Have to set the mixer FPU mode since that's what the resampler code expects. */
|
||||
START_MIXER_MODE();
|
||||
step = (ALsizei)mind(((ALdouble)srcRate/dstRate*FRACTIONONE) + 0.5,
|
||||
MAX_PITCH * FRACTIONONE);
|
||||
converter->mIncrement = maxi(step, 1);
|
||||
if(converter->mIncrement == FRACTIONONE)
|
||||
converter->mResample = Resample_copy_C;
|
||||
else
|
||||
{
|
||||
/* TODO: Allow other resamplers. */
|
||||
BsincPrepare(converter->mIncrement, &converter->mState.bsinc, &bsinc12);
|
||||
converter->mResample = SelectResampler(BSinc12Resampler);
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
void DestroySampleConverter(SampleConverter **converter)
|
||||
{
|
||||
if(converter)
|
||||
{
|
||||
al_free(*converter);
|
||||
*converter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/128.0f); }
|
||||
static inline ALfloat Sample_ALubyte(ALubyte val)
|
||||
{ return Sample_ALbyte((ALint)val - 128); }
|
||||
|
||||
static inline ALfloat Sample_ALshort(ALshort val)
|
||||
{ return val * (1.0f/32768.0f); }
|
||||
static inline ALfloat Sample_ALushort(ALushort val)
|
||||
{ return Sample_ALshort((ALint)val - 32768); }
|
||||
|
||||
static inline ALfloat Sample_ALint(ALint val)
|
||||
{ return (val>>7) * (1.0f/16777216.0f); }
|
||||
static inline ALfloat Sample_ALuint(ALuint val)
|
||||
{ return Sample_ALint(val - INT_MAX - 1); }
|
||||
|
||||
static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
|
||||
ALint srcstep, ALsizei samples) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i] = Sample_##T(src[i*srcstep]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum DevFmtType srctype, ALsizei samples)
|
||||
{
|
||||
switch(srctype)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Load_ALbyte(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Load_ALubyte(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Load_ALshort(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Load_ALushort(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Load_ALint(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Load_ALuint(dst, src, srcstep, samples);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Load_ALfloat(dst, src, srcstep, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline ALbyte ALbyte_Sample(ALfloat val)
|
||||
{ return fastf2i(clampf(val*128.0f, -128.0f, 127.0f)); }
|
||||
static inline ALubyte ALubyte_Sample(ALfloat val)
|
||||
{ return ALbyte_Sample(val)+128; }
|
||||
|
||||
static inline ALshort ALshort_Sample(ALfloat val)
|
||||
{ return fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f)); }
|
||||
static inline ALushort ALushort_Sample(ALfloat val)
|
||||
{ return ALshort_Sample(val)+32768; }
|
||||
|
||||
static inline ALint ALint_Sample(ALfloat val)
|
||||
{ return fastf2i(clampf(val*16777216.0f, -16777216.0f, 16777215.0f)) << 7; }
|
||||
static inline ALuint ALuint_Sample(ALfloat val)
|
||||
{ return ALint_Sample(val)+INT_MAX+1; }
|
||||
|
||||
static inline ALfloat ALfloat_Sample(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline void Store_##T(T *restrict dst, const ALfloat *restrict src, \
|
||||
ALint dststep, ALsizei samples) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i*dststep] = T##_Sample(src[i]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void StoreSamples(ALvoid *dst, const ALfloat *src, ALint dststep, enum DevFmtType dsttype, ALsizei samples)
|
||||
{
|
||||
switch(dsttype)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Store_ALbyte(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Store_ALubyte(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Store_ALshort(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Store_ALushort(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Store_ALint(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Store_ALuint(dst, src, dststep, samples);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Store_ALfloat(dst, src, dststep, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes)
|
||||
{
|
||||
ALint prepcount = converter->mSrcPrepCount;
|
||||
ALsizei increment = converter->mIncrement;
|
||||
ALsizei DataPosFrac = converter->mFracOffset;
|
||||
ALuint64 DataSize64;
|
||||
|
||||
if(prepcount < 0)
|
||||
{
|
||||
/* Negative prepcount means we need to skip that many input samples. */
|
||||
if(-prepcount >= srcframes)
|
||||
return 0;
|
||||
srcframes += prepcount;
|
||||
prepcount = 0;
|
||||
}
|
||||
|
||||
if(srcframes < 1)
|
||||
{
|
||||
/* No output samples if there's no input samples. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(prepcount < MAX_RESAMPLE_PADDING*2 &&
|
||||
MAX_RESAMPLE_PADDING*2 - prepcount >= srcframes)
|
||||
{
|
||||
/* Not enough input samples to generate an output sample. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
DataSize64 = prepcount;
|
||||
DataSize64 += srcframes;
|
||||
DataSize64 -= MAX_RESAMPLE_PADDING*2;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
/* If we have a full prep, we can generate at least one sample. */
|
||||
return (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
|
||||
}
|
||||
|
||||
|
||||
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes)
|
||||
{
|
||||
const ALsizei SrcFrameSize = converter->mNumChannels * converter->mSrcTypeSize;
|
||||
const ALsizei DstFrameSize = converter->mNumChannels * converter->mDstTypeSize;
|
||||
const ALsizei increment = converter->mIncrement;
|
||||
ALsizei pos = 0;
|
||||
|
||||
START_MIXER_MODE();
|
||||
while(pos < dstframes && *srcframes > 0)
|
||||
{
|
||||
ALfloat *restrict SrcData = ASSUME_ALIGNED(converter->mSrcSamples, 16);
|
||||
ALfloat *restrict DstData = ASSUME_ALIGNED(converter->mDstSamples, 16);
|
||||
ALint prepcount = converter->mSrcPrepCount;
|
||||
ALsizei DataPosFrac = converter->mFracOffset;
|
||||
ALuint64 DataSize64;
|
||||
ALsizei DstSize;
|
||||
ALint toread;
|
||||
ALsizei chan;
|
||||
|
||||
if(prepcount < 0)
|
||||
{
|
||||
/* Negative prepcount means we need to skip that many input samples. */
|
||||
if(-prepcount >= *srcframes)
|
||||
{
|
||||
converter->mSrcPrepCount = prepcount + *srcframes;
|
||||
*srcframes = 0;
|
||||
break;
|
||||
}
|
||||
*src = (const ALbyte*)*src + SrcFrameSize*-prepcount;
|
||||
*srcframes += prepcount;
|
||||
converter->mSrcPrepCount = 0;
|
||||
continue;
|
||||
}
|
||||
toread = mini(*srcframes, BUFFERSIZE - MAX_RESAMPLE_PADDING*2);
|
||||
|
||||
if(prepcount < MAX_RESAMPLE_PADDING*2 &&
|
||||
MAX_RESAMPLE_PADDING*2 - prepcount >= toread)
|
||||
{
|
||||
/* Not enough input samples to generate an output sample. Store
|
||||
* what we're given for later.
|
||||
*/
|
||||
for(chan = 0;chan < converter->mNumChannels;chan++)
|
||||
LoadSamples(&converter->Chan[chan].mPrevSamples[prepcount],
|
||||
(const ALbyte*)*src + converter->mSrcTypeSize*chan,
|
||||
converter->mNumChannels, converter->mSrcType, toread
|
||||
);
|
||||
|
||||
converter->mSrcPrepCount = prepcount + toread;
|
||||
*srcframes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataSize64 = prepcount;
|
||||
DataSize64 += toread;
|
||||
DataSize64 -= MAX_RESAMPLE_PADDING*2;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
/* If we have a full prep, we can generate at least one sample. */
|
||||
DstSize = (ALsizei)clampu64((DataSize64 + increment-1)/increment, 1, BUFFERSIZE);
|
||||
DstSize = mini(DstSize, dstframes-pos);
|
||||
|
||||
for(chan = 0;chan < converter->mNumChannels;chan++)
|
||||
{
|
||||
const ALbyte *SrcSamples = (const ALbyte*)*src + converter->mSrcTypeSize*chan;
|
||||
ALbyte *DstSamples = (ALbyte*)dst + converter->mDstTypeSize*chan;
|
||||
const ALfloat *ResampledData;
|
||||
ALsizei SrcDataEnd;
|
||||
|
||||
/* Load the previous samples into the source data first, then the
|
||||
* new samples from the input buffer.
|
||||
*/
|
||||
memcpy(SrcData, converter->Chan[chan].mPrevSamples,
|
||||
prepcount*sizeof(ALfloat));
|
||||
LoadSamples(SrcData + prepcount, SrcSamples,
|
||||
converter->mNumChannels, converter->mSrcType, toread
|
||||
);
|
||||
|
||||
/* Store as many prep samples for next time as possible, given the
|
||||
* number of output samples being generated.
|
||||
*/
|
||||
SrcDataEnd = (DataPosFrac + increment*DstSize)>>FRACTIONBITS;
|
||||
if(SrcDataEnd >= prepcount+toread)
|
||||
memset(converter->Chan[chan].mPrevSamples, 0,
|
||||
sizeof(converter->Chan[chan].mPrevSamples));
|
||||
else
|
||||
{
|
||||
size_t len = mini(MAX_RESAMPLE_PADDING*2, prepcount+toread-SrcDataEnd);
|
||||
memcpy(converter->Chan[chan].mPrevSamples, &SrcData[SrcDataEnd],
|
||||
len*sizeof(ALfloat));
|
||||
memset(converter->Chan[chan].mPrevSamples+len, 0,
|
||||
sizeof(converter->Chan[chan].mPrevSamples) - len*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
/* Now resample, and store the result in the output buffer. */
|
||||
ResampledData = converter->mResample(&converter->mState,
|
||||
SrcData+MAX_RESAMPLE_PADDING, DataPosFrac, increment,
|
||||
DstData, DstSize
|
||||
);
|
||||
|
||||
StoreSamples(DstSamples, ResampledData, converter->mNumChannels,
|
||||
converter->mDstType, DstSize);
|
||||
}
|
||||
|
||||
/* Update the number of prep samples still available, as well as the
|
||||
* fractional offset.
|
||||
*/
|
||||
DataPosFrac += increment*DstSize;
|
||||
converter->mSrcPrepCount = mini(prepcount + toread - (DataPosFrac>>FRACTIONBITS),
|
||||
MAX_RESAMPLE_PADDING*2);
|
||||
converter->mFracOffset = DataPosFrac & FRACTIONMASK;
|
||||
|
||||
/* Update the src and dst pointers in case there's still more to do. */
|
||||
*src = (const ALbyte*)*src + SrcFrameSize*(DataPosFrac>>FRACTIONBITS);
|
||||
*srcframes -= mini(*srcframes, (DataPosFrac>>FRACTIONBITS));
|
||||
|
||||
dst = (ALbyte*)dst + DstFrameSize*DstSize;
|
||||
pos += DstSize;
|
||||
}
|
||||
END_MIXER_MODE();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans)
|
||||
{
|
||||
ChannelConverter *converter;
|
||||
|
||||
if(srcChans != dstChans && !((srcChans == DevFmtMono && dstChans == DevFmtStereo) ||
|
||||
(srcChans == DevFmtStereo && dstChans == DevFmtMono)))
|
||||
return NULL;
|
||||
|
||||
converter = al_calloc(DEF_ALIGN, sizeof(*converter));
|
||||
converter->mSrcType = srcType;
|
||||
converter->mSrcChans = srcChans;
|
||||
converter->mDstChans = dstChans;
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
void DestroyChannelConverter(ChannelConverter **converter)
|
||||
{
|
||||
if(converter)
|
||||
{
|
||||
al_free(*converter);
|
||||
*converter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static void Mono2Stereo##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < frames;i++) \
|
||||
dst[i*2 + 1] = dst[i*2 + 0] = Sample_##T(src[i]) * 0.707106781187f; \
|
||||
} \
|
||||
\
|
||||
static void Stereo2Mono##T(ALfloat *restrict dst, const T *src, ALsizei frames)\
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < frames;i++) \
|
||||
dst[i] = (Sample_##T(src[i*2 + 0])+Sample_##T(src[i*2 + 1])) * \
|
||||
0.707106781187f; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALushort)
|
||||
DECL_TEMPLATE(ALint)
|
||||
DECL_TEMPLATE(ALuint)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames)
|
||||
{
|
||||
if(converter->mSrcChans == converter->mDstChans)
|
||||
{
|
||||
LoadSamples(dst, src, 1, converter->mSrcType,
|
||||
frames*ChannelsFromDevFmt(converter->mSrcChans, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if(converter->mSrcChans == DevFmtStereo && converter->mDstChans == DevFmtMono)
|
||||
{
|
||||
switch(converter->mSrcType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Stereo2MonoALbyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Stereo2MonoALubyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Stereo2MonoALshort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Stereo2MonoALushort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Stereo2MonoALint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Stereo2MonoALuint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Stereo2MonoALfloat(dst, src, frames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /*if(converter->mSrcChans == DevFmtMono && converter->mDstChans == DevFmtStereo)*/
|
||||
{
|
||||
switch(converter->mSrcType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
Mono2StereoALbyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
Mono2StereoALubyte(dst, src, frames);
|
||||
break;
|
||||
case DevFmtShort:
|
||||
Mono2StereoALshort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
Mono2StereoALushort(dst, src, frames);
|
||||
break;
|
||||
case DevFmtInt:
|
||||
Mono2StereoALint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
Mono2StereoALuint(dst, src, frames);
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
Mono2StereoALfloat(dst, src, frames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef CONVERTER_H
|
||||
#define CONVERTER_H
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifdef __cpluspluc
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SampleConverter {
|
||||
enum DevFmtType mSrcType;
|
||||
enum DevFmtType mDstType;
|
||||
ALsizei mNumChannels;
|
||||
ALsizei mSrcTypeSize;
|
||||
ALsizei mDstTypeSize;
|
||||
|
||||
ALint mSrcPrepCount;
|
||||
|
||||
ALsizei mFracOffset;
|
||||
ALsizei mIncrement;
|
||||
InterpState mState;
|
||||
ResamplerFunc mResample;
|
||||
|
||||
alignas(16) ALfloat mSrcSamples[BUFFERSIZE];
|
||||
alignas(16) ALfloat mDstSamples[BUFFERSIZE];
|
||||
|
||||
struct {
|
||||
alignas(16) ALfloat mPrevSamples[MAX_RESAMPLE_PADDING*2];
|
||||
} Chan[];
|
||||
} SampleConverter;
|
||||
|
||||
SampleConverter *CreateSampleConverter(enum DevFmtType srcType, enum DevFmtType dstType, ALsizei numchans, ALsizei srcRate, ALsizei dstRate);
|
||||
void DestroySampleConverter(SampleConverter **converter);
|
||||
|
||||
ALsizei SampleConverterInput(SampleConverter *converter, const ALvoid **src, ALsizei *srcframes, ALvoid *dst, ALsizei dstframes);
|
||||
ALsizei SampleConverterAvailableOut(SampleConverter *converter, ALsizei srcframes);
|
||||
|
||||
|
||||
typedef struct ChannelConverter {
|
||||
enum DevFmtType mSrcType;
|
||||
enum DevFmtChannels mSrcChans;
|
||||
enum DevFmtChannels mDstChans;
|
||||
} ChannelConverter;
|
||||
|
||||
ChannelConverter *CreateChannelConverter(enum DevFmtType srcType, enum DevFmtChannels srcChans, enum DevFmtChannels dstChans);
|
||||
void DestroyChannelConverter(ChannelConverter **converter);
|
||||
|
||||
void ChannelConverterInput(ChannelConverter *converter, const ALvoid *src, ALfloat *dst, ALsizei frames);
|
||||
|
||||
#ifdef __cpluspluc
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONVERTER_H */
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef CPU_CAPS_H
|
||||
#define CPU_CAPS_H
|
||||
|
||||
extern int CPUCapFlags;
|
||||
enum {
|
||||
CPU_CAP_SSE = 1<<0,
|
||||
CPU_CAP_SSE2 = 1<<1,
|
||||
CPU_CAP_SSE3 = 1<<2,
|
||||
CPU_CAP_SSE4_1 = 1<<3,
|
||||
CPU_CAP_NEON = 1<<4,
|
||||
};
|
||||
|
||||
void FillCPUCaps(int capfilter);
|
||||
|
||||
#endif /* CPU_CAPS_H */
|
||||
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2018 by Raul Herraiz.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
#define MIN_FREQ 20.0f
|
||||
#define MAX_FREQ 2500.0f
|
||||
#define Q_FACTOR 5.0f
|
||||
|
||||
typedef struct ALautowahState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect parameters */
|
||||
ALfloat AttackRate;
|
||||
ALfloat ReleaseRate;
|
||||
ALfloat ResonanceGain;
|
||||
ALfloat PeakGain;
|
||||
ALfloat FreqMinNorm;
|
||||
ALfloat BandwidthNorm;
|
||||
ALfloat env_delay;
|
||||
|
||||
/* Filter components derived from the envelope. */
|
||||
struct {
|
||||
ALfloat cos_w0;
|
||||
ALfloat alpha;
|
||||
} Env[BUFFERSIZE];
|
||||
|
||||
struct {
|
||||
/* Effect filters' history. */
|
||||
struct {
|
||||
ALfloat z1, z2;
|
||||
} Filter;
|
||||
|
||||
/* Effect gains for each output channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
/* Effects buffers */
|
||||
alignas(16) ALfloat BufferOut[BUFFERSIZE];
|
||||
} ALautowahState;
|
||||
|
||||
static ALvoid ALautowahState_Destruct(ALautowahState *state);
|
||||
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
|
||||
static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
|
||||
|
||||
static void ALautowahState_Construct(ALautowahState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALautowahState, ALeffectState, state);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_Destruct(ALautowahState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
ALsizei i, j;
|
||||
|
||||
state->AttackRate = 1.0f;
|
||||
state->ReleaseRate = 1.0f;
|
||||
state->ResonanceGain = 10.0f;
|
||||
state->PeakGain = 4.5f;
|
||||
state->FreqMinNorm = 4.5e-4f;
|
||||
state->BandwidthNorm = 0.05f;
|
||||
state->env_delay = 0.0f;
|
||||
|
||||
memset(state->Env, 0, sizeof(state->Env));
|
||||
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
state->Chans[i].Filter.z1 = 0.0f;
|
||||
state->Chans[i].Filter.z2 = 0.0f;
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat ReleaseTime;
|
||||
ALsizei i;
|
||||
|
||||
ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
|
||||
|
||||
state->AttackRate = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
|
||||
state->ReleaseRate = expf(-1.0f / (ReleaseTime*device->Frequency));
|
||||
/* 0-20dB Resonance Peak gain */
|
||||
state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
|
||||
state->PeakGain = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
|
||||
state->FreqMinNorm = MIN_FREQ / device->Frequency;
|
||||
state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALfloat attack_rate = state->AttackRate;
|
||||
const ALfloat release_rate = state->ReleaseRate;
|
||||
const ALfloat res_gain = state->ResonanceGain;
|
||||
const ALfloat peak_gain = state->PeakGain;
|
||||
const ALfloat freq_min = state->FreqMinNorm;
|
||||
const ALfloat bandwidth = state->BandwidthNorm;
|
||||
ALfloat env_delay;
|
||||
ALsizei c, i;
|
||||
|
||||
env_delay = state->env_delay;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat w0, sample, a;
|
||||
|
||||
/* Envelope follower described on the book: Audio Effects, Theory,
|
||||
* Implementation and Application.
|
||||
*/
|
||||
sample = peak_gain * fabsf(SamplesIn[0][i]);
|
||||
a = (sample > env_delay) ? attack_rate : release_rate;
|
||||
env_delay = lerp(sample, env_delay, a);
|
||||
|
||||
/* Calculate the cos and alpha components for this sample's filter. */
|
||||
w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
|
||||
state->Env[i].cos_w0 = cosf(w0);
|
||||
state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
|
||||
}
|
||||
state->env_delay = env_delay;
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
|
||||
{
|
||||
/* This effectively inlines BiquadFilter_setParams for a peaking
|
||||
* filter and BiquadFilter_processC. The alpha and cosine components
|
||||
* for the filter coefficients were previously calculated with the
|
||||
* envelope. Because the filter changes for each sample, the
|
||||
* coefficients are transient and don't need to be held.
|
||||
*/
|
||||
ALfloat z1 = state->Chans[c].Filter.z1;
|
||||
ALfloat z2 = state->Chans[c].Filter.z2;
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALfloat alpha = state->Env[i].alpha;
|
||||
const ALfloat cos_w0 = state->Env[i].cos_w0;
|
||||
ALfloat input, output;
|
||||
ALfloat a[3], b[3];
|
||||
|
||||
b[0] = 1.0f + alpha*res_gain;
|
||||
b[1] = -2.0f * cos_w0;
|
||||
b[2] = 1.0f - alpha*res_gain;
|
||||
a[0] = 1.0f + alpha/res_gain;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha/res_gain;
|
||||
|
||||
input = SamplesIn[c][i];
|
||||
output = input*(b[0]/a[0]) + z1;
|
||||
z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
|
||||
z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
|
||||
state->BufferOut[i] = output;
|
||||
}
|
||||
state->Chans[c].Filter.z1 = z1;
|
||||
state->Chans[c].Filter.z2 = z2;
|
||||
|
||||
/* Now, mix the processed sound data to the output. */
|
||||
MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct AutowahStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} AutowahStateFactory;
|
||||
|
||||
static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALautowahState *state;
|
||||
|
||||
NEW_OBJ0(state, ALautowahState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
|
||||
|
||||
EffectStateFactory *AutowahStateFactory_getFactory(void)
|
||||
{
|
||||
static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &AutowahFactory);
|
||||
}
|
||||
|
||||
void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
|
||||
props->Autowah.AttackTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
|
||||
props->Autowah.ReleaseTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
|
||||
props->Autowah.Resonance = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
|
||||
props->Autowah.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
|
||||
void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALautowah_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
|
||||
}
|
||||
|
||||
void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
|
||||
}
|
||||
|
||||
void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
|
||||
}
|
||||
void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
|
||||
}
|
||||
|
||||
void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALautowah_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALautowah);
|
||||
@@ -0,0 +1,555 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
||||
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||
|
||||
enum WaveForm {
|
||||
WF_Sinusoid,
|
||||
WF_Triangle
|
||||
};
|
||||
|
||||
typedef struct ALchorusState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALsizei BufferLength;
|
||||
ALsizei offset;
|
||||
|
||||
ALsizei lfo_offset;
|
||||
ALsizei lfo_range;
|
||||
ALfloat lfo_scale;
|
||||
ALint lfo_disp;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat Target[MAX_OUTPUT_CHANNELS];
|
||||
} Gains[2];
|
||||
|
||||
/* effect parameters */
|
||||
enum WaveForm waveform;
|
||||
ALint delay;
|
||||
ALfloat depth;
|
||||
ALfloat feedback;
|
||||
} ALchorusState;
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state);
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device);
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props);
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALchorusState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALchorusState);
|
||||
|
||||
|
||||
static void ALchorusState_Construct(ALchorusState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALchorusState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer = NULL;
|
||||
state->offset = 0;
|
||||
state->lfo_offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->waveform = WF_Triangle;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_Destruct(ALchorusState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device)
|
||||
{
|
||||
const ALfloat max_delay = maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY);
|
||||
ALsizei maxlen;
|
||||
|
||||
maxlen = NextPowerOf2(float2int(max_delay*2.0f*Device->Frequency) + 1u);
|
||||
if(maxlen <= 0) return AL_FALSE;
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = temp;
|
||||
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
|
||||
memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
|
||||
memset(state->Gains, 0, sizeof(state->Gains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALchorusState_update(ALchorusState *state, const ALCcontext *Context, const ALeffectslot *Slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALsizei mindelay = MAX_RESAMPLE_PADDING << FRACTIONBITS;
|
||||
const ALCdevice *device = Context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat rate;
|
||||
ALint phase;
|
||||
|
||||
switch(props->Chorus.Waveform)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE:
|
||||
state->waveform = WF_Triangle;
|
||||
break;
|
||||
case AL_CHORUS_WAVEFORM_SINUSOID:
|
||||
state->waveform = WF_Sinusoid;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The LFO depth is scaled to be relative to the sample delay. Clamp the
|
||||
* delay and depth to allow enough padding for resampling.
|
||||
*/
|
||||
state->delay = maxi(float2int(props->Chorus.Delay*frequency*FRACTIONONE + 0.5f),
|
||||
mindelay);
|
||||
state->depth = minf(props->Chorus.Depth * state->delay,
|
||||
(ALfloat)(state->delay - mindelay));
|
||||
|
||||
state->feedback = props->Chorus.Feedback;
|
||||
|
||||
/* Gains for left and right sides */
|
||||
CalcAngleCoeffs(-F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[0].Target);
|
||||
CalcAngleCoeffs( F_PI_2, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, Slot->Params.Gain, state->Gains[1].Target);
|
||||
|
||||
phase = props->Chorus.Phase;
|
||||
rate = props->Chorus.Rate;
|
||||
if(!(rate > 0.0f))
|
||||
{
|
||||
state->lfo_offset = 0;
|
||||
state->lfo_range = 1;
|
||||
state->lfo_scale = 0.0f;
|
||||
state->lfo_disp = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Calculate LFO coefficient (number of samples per cycle). Limit the
|
||||
* max range to avoid overflow when calculating the displacement.
|
||||
*/
|
||||
ALsizei lfo_range = float2int(minf(frequency/rate + 0.5f, (ALfloat)(INT_MAX/360 - 180)));
|
||||
|
||||
state->lfo_offset = float2int((ALfloat)state->lfo_offset/state->lfo_range*
|
||||
lfo_range + 0.5f) % lfo_range;
|
||||
state->lfo_range = lfo_range;
|
||||
switch(state->waveform)
|
||||
{
|
||||
case WF_Triangle:
|
||||
state->lfo_scale = 4.0f / state->lfo_range;
|
||||
break;
|
||||
case WF_Sinusoid:
|
||||
state->lfo_scale = F_TAU / state->lfo_range;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate lfo phase displacement */
|
||||
if(phase < 0) phase = 360 + phase;
|
||||
state->lfo_disp = (state->lfo_range*phase + 180) / 360;
|
||||
}
|
||||
}
|
||||
|
||||
static void GetTriangleDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i((1.0f - fabsf(2.0f - lfo_scale*offset)) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
static void GetSinusoidDelays(ALint *restrict delays, ALsizei offset, const ALsizei lfo_range,
|
||||
const ALfloat lfo_scale, const ALfloat depth, const ALsizei delay,
|
||||
const ALsizei todo)
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
delays[i] = fastf2i(sinf(lfo_scale*offset) * depth) + delay;
|
||||
offset = (offset+1)%lfo_range;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ALvoid ALchorusState_process(ALchorusState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALsizei bufmask = state->BufferLength-1;
|
||||
const ALfloat feedback = state->feedback;
|
||||
const ALsizei avgdelay = (state->delay + (FRACTIONONE>>1)) >> FRACTIONBITS;
|
||||
ALfloat *restrict delaybuf = state->SampleBuffer;
|
||||
ALsizei offset = state->offset;
|
||||
ALsizei i, c;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
const ALsizei todo = mini(256, SamplesToDo-base);
|
||||
ALint moddelays[2][256];
|
||||
alignas(16) ALfloat temps[2][256];
|
||||
|
||||
if(state->waveform == WF_Sinusoid)
|
||||
{
|
||||
GetSinusoidDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
|
||||
state->depth, state->delay, todo);
|
||||
GetSinusoidDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
}
|
||||
else /*if(state->waveform == WF_Triangle)*/
|
||||
{
|
||||
GetTriangleDelays(moddelays[0], state->lfo_offset, state->lfo_range, state->lfo_scale,
|
||||
state->depth, state->delay, todo);
|
||||
GetTriangleDelays(moddelays[1], (state->lfo_offset+state->lfo_disp)%state->lfo_range,
|
||||
state->lfo_range, state->lfo_scale, state->depth, state->delay,
|
||||
todo);
|
||||
}
|
||||
state->lfo_offset = (state->lfo_offset+todo) % state->lfo_range;
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALint delay;
|
||||
ALfloat mu;
|
||||
|
||||
// Feed the buffer's input first (necessary for delays < 1).
|
||||
delaybuf[offset&bufmask] = SamplesIn[0][base+i];
|
||||
|
||||
// Tap for the left output.
|
||||
delay = offset - (moddelays[0][i]>>FRACTIONBITS);
|
||||
mu = (moddelays[0][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
|
||||
temps[0][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
|
||||
delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
|
||||
mu);
|
||||
|
||||
// Tap for the right output.
|
||||
delay = offset - (moddelays[1][i]>>FRACTIONBITS);
|
||||
mu = (moddelays[1][i]&FRACTIONMASK) * (1.0f/FRACTIONONE);
|
||||
temps[1][i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask],
|
||||
delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask],
|
||||
mu);
|
||||
|
||||
// Accumulate feedback from the average delay of the taps.
|
||||
delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < 2;c++)
|
||||
MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
|
||||
state->Gains[c].Target, SamplesToDo-base, base, todo);
|
||||
|
||||
base += todo;
|
||||
}
|
||||
|
||||
state->offset = offset;
|
||||
}
|
||||
|
||||
|
||||
typedef struct ChorusStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} ChorusStateFactory;
|
||||
|
||||
static ALeffectState *ChorusStateFactory_create(ChorusStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
NEW_OBJ0(state, ALchorusState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(ChorusStateFactory);
|
||||
|
||||
|
||||
EffectStateFactory *ChorusStateFactory_getFactory(void)
|
||||
{
|
||||
static ChorusStateFactory ChorusFactory = { { GET_VTABLE2(ChorusStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &ChorusFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALchorus_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
if(!(val >= AL_CHORUS_MIN_WAVEFORM && val <= AL_CHORUS_MAX_WAVEFORM))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid chorus waveform");
|
||||
props->Chorus.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus phase out of range");
|
||||
props->Chorus.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{ ALchorus_setParami(effect, context, param, vals[0]); }
|
||||
void ALchorus_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus rate out of range");
|
||||
props->Chorus.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus depth out of range");
|
||||
props->Chorus.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus feedback out of range");
|
||||
props->Chorus.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Chorus delay out of range");
|
||||
props->Chorus.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALchorus_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALchorus_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
*val = props->Chorus.Waveform;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{ ALchorus_getParami(effect, context, param, vals); }
|
||||
void ALchorus_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALchorus_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALchorus_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALchorus);
|
||||
|
||||
|
||||
/* Flanger is basically a chorus with a really short delay. They can both use
|
||||
* the same processing functions, so piggyback flanger on the chorus functions.
|
||||
*/
|
||||
typedef struct FlangerStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} FlangerStateFactory;
|
||||
|
||||
ALeffectState *FlangerStateFactory_create(FlangerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALchorusState *state;
|
||||
|
||||
NEW_OBJ0(state, ALchorusState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(FlangerStateFactory);
|
||||
|
||||
EffectStateFactory *FlangerStateFactory_getFactory(void)
|
||||
{
|
||||
static FlangerStateFactory FlangerFactory = { { GET_VTABLE2(FlangerStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &FlangerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALflanger_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
if(!(val >= AL_FLANGER_MIN_WAVEFORM && val <= AL_FLANGER_MAX_WAVEFORM))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid flanger waveform");
|
||||
props->Chorus.Waveform = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger phase out of range");
|
||||
props->Chorus.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{ ALflanger_setParami(effect, context, param, vals[0]); }
|
||||
void ALflanger_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger rate out of range");
|
||||
props->Chorus.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger depth out of range");
|
||||
props->Chorus.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger feedback out of range");
|
||||
props->Chorus.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Flanger delay out of range");
|
||||
props->Chorus.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALflanger_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALflanger_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
*val = props->Chorus.Waveform;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{ ALflanger_getParami(effect, context, param, vals); }
|
||||
void ALflanger_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALflanger_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALflanger_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALflanger);
|
||||
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Anis A. Hireche
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "alError.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alu.h"
|
||||
|
||||
|
||||
#define AMP_ENVELOPE_MIN 0.5f
|
||||
#define AMP_ENVELOPE_MAX 2.0f
|
||||
|
||||
#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
|
||||
#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
|
||||
|
||||
|
||||
typedef struct ALcompressorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MAX_EFFECT_CHANNELS][MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
ALboolean Enabled;
|
||||
ALfloat AttackMult;
|
||||
ALfloat ReleaseMult;
|
||||
ALfloat EnvFollower;
|
||||
} ALcompressorState;
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state);
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device);
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALcompressorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALcompressorState);
|
||||
|
||||
|
||||
static void ALcompressorState_Construct(ALcompressorState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALcompressorState, ALeffectState, state);
|
||||
|
||||
state->Enabled = AL_TRUE;
|
||||
state->AttackMult = 1.0f;
|
||||
state->ReleaseMult = 1.0f;
|
||||
state->EnvFollower = 1.0f;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_Destruct(ALcompressorState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALcompressorState_deviceUpdate(ALcompressorState *state, ALCdevice *device)
|
||||
{
|
||||
/* Number of samples to do a full attack and release (non-integer sample
|
||||
* counts are okay).
|
||||
*/
|
||||
const ALfloat attackCount = (ALfloat)device->Frequency * ATTACK_TIME;
|
||||
const ALfloat releaseCount = (ALfloat)device->Frequency * RELEASE_TIME;
|
||||
|
||||
/* Calculate per-sample multipliers to attack and release at the desired
|
||||
* rates.
|
||||
*/
|
||||
state->AttackMult = powf(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
|
||||
state->ReleaseMult = powf(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_update(ALcompressorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALuint i;
|
||||
|
||||
state->Enabled = props->Compressor.OnOff;
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < 4;i++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain, state->Gain[i]);
|
||||
}
|
||||
|
||||
static ALvoid ALcompressorState_process(ALcompressorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALsizei i, j, k;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALfloat gains[256];
|
||||
ALsizei td = mini(256, SamplesToDo-base);
|
||||
ALfloat env = state->EnvFollower;
|
||||
|
||||
/* Generate the per-sample gains from the signal envelope. */
|
||||
if(state->Enabled)
|
||||
{
|
||||
for(i = 0;i < td;++i)
|
||||
{
|
||||
/* Clamp the absolute amplitude to the defined envelope limits,
|
||||
* then attack or release the envelope to reach it.
|
||||
*/
|
||||
ALfloat amplitude = clampf(fabsf(SamplesIn[0][base+i]),
|
||||
AMP_ENVELOPE_MIN, AMP_ENVELOPE_MAX);
|
||||
if(amplitude > env)
|
||||
env = minf(env*state->AttackMult, amplitude);
|
||||
else if(amplitude < env)
|
||||
env = maxf(env*state->ReleaseMult, amplitude);
|
||||
|
||||
/* Apply the reciprocal of the envelope to normalize the volume
|
||||
* (compress the dynamic range).
|
||||
*/
|
||||
gains[i] = 1.0f / env;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Same as above, except the amplitude is forced to 1. This helps
|
||||
* ensure smooth gain changes when the compressor is turned on and
|
||||
* off.
|
||||
*/
|
||||
for(i = 0;i < td;++i)
|
||||
{
|
||||
ALfloat amplitude = 1.0f;
|
||||
if(amplitude > env)
|
||||
env = minf(env*state->AttackMult, amplitude);
|
||||
else if(amplitude < env)
|
||||
env = maxf(env*state->ReleaseMult, amplitude);
|
||||
|
||||
gains[i] = 1.0f / env;
|
||||
}
|
||||
}
|
||||
state->EnvFollower = env;
|
||||
|
||||
/* Now compress the signal amplitude to output. */
|
||||
for(j = 0;j < MAX_EFFECT_CHANNELS;j++)
|
||||
{
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
ALfloat gain = state->Gain[j][k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
SamplesOut[k][base+i] += SamplesIn[j][base+i] * gains[i] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct CompressorStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} CompressorStateFactory;
|
||||
|
||||
static ALeffectState *CompressorStateFactory_create(CompressorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALcompressorState *state;
|
||||
|
||||
NEW_OBJ0(state, ALcompressorState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(CompressorStateFactory);
|
||||
|
||||
EffectStateFactory *CompressorStateFactory_getFactory(void)
|
||||
{
|
||||
static CompressorStateFactory CompressorFactory = { { GET_VTABLE2(CompressorStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &CompressorFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALcompressor_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
|
||||
props->Compressor.OnOff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALcompressor_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{ ALcompressor_setParami(effect, context, param, vals[0]); }
|
||||
void ALcompressor_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
|
||||
void ALcompressor_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
|
||||
|
||||
void ALcompressor_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
*val = props->Compressor.OnOff;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALcompressor_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{ ALcompressor_getParami(effect, context, param, vals); }
|
||||
void ALcompressor_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
|
||||
void ALcompressor_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALcompressor);
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2011 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALdedicatedState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} ALdedicatedState;
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state);
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *device);
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdedicatedState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdedicatedState);
|
||||
|
||||
|
||||
static void ALdedicatedState_Construct(ALdedicatedState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdedicatedState, ALeffectState, state);
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_Destruct(ALdedicatedState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdedicatedState_deviceUpdate(ALdedicatedState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
ALsizei i;
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->CurrentGains[i] = 0.0f;
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_update(ALdedicatedState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat Gain;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < MAX_OUTPUT_CHANNELS;i++)
|
||||
state->TargetGains[i] = 0.0f;
|
||||
|
||||
Gain = slot->Params.Gain * props->Dedicated.Gain;
|
||||
if(slot->Params.EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT)
|
||||
{
|
||||
int idx;
|
||||
if((idx=GetChannelIdxByName(&device->RealOut, LFE)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->TargetGains[idx] = Gain;
|
||||
}
|
||||
}
|
||||
else if(slot->Params.EffectType == AL_EFFECT_DEDICATED_DIALOGUE)
|
||||
{
|
||||
int idx;
|
||||
/* Dialog goes to the front-center speaker if it exists, otherwise it
|
||||
* plays from the front-center location. */
|
||||
if((idx=GetChannelIdxByName(&device->RealOut, FrontCenter)) != -1)
|
||||
{
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->RealOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->RealOut.NumChannels;
|
||||
state->TargetGains[idx] = Gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->Dry.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->Dry.NumChannels;
|
||||
ComputePanGains(&device->Dry, coeffs, Gain, state->TargetGains);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ALvoid ALdedicatedState_process(ALdedicatedState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
MixSamples(SamplesIn[0], NumChannels, SamplesOut, state->CurrentGains,
|
||||
state->TargetGains, SamplesToDo, 0, SamplesToDo);
|
||||
}
|
||||
|
||||
|
||||
typedef struct DedicatedStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} DedicatedStateFactory;
|
||||
|
||||
ALeffectState *DedicatedStateFactory_create(DedicatedStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdedicatedState *state;
|
||||
|
||||
NEW_OBJ0(state, ALdedicatedState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(DedicatedStateFactory);
|
||||
|
||||
|
||||
EffectStateFactory *DedicatedStateFactory_getFactory(void)
|
||||
{
|
||||
static DedicatedStateFactory DedicatedFactory = { { GET_VTABLE2(DedicatedStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &DedicatedFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALdedicated_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
|
||||
void ALdedicated_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
|
||||
void ALdedicated_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
if(!(val >= 0.0f && isfinite(val)))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Dedicated gain out of range");
|
||||
props->Dedicated.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALdedicated_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALdedicated_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALdedicated_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param); }
|
||||
void ALdedicated_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", param); }
|
||||
void ALdedicated_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
*val = props->Dedicated.Gain;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALdedicated_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALdedicated_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALdedicated);
|
||||
@@ -0,0 +1,286 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALdistortionState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat Gain[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
/* Effect parameters */
|
||||
BiquadFilter lowpass;
|
||||
BiquadFilter bandpass;
|
||||
ALfloat attenuation;
|
||||
ALfloat edge_coeff;
|
||||
|
||||
ALfloat Buffer[2][BUFFERSIZE];
|
||||
} ALdistortionState;
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state);
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *device);
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALdistortionState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALdistortionState);
|
||||
|
||||
|
||||
static void ALdistortionState_Construct(ALdistortionState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALdistortionState, ALeffectState, state);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_Destruct(ALdistortionState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALdistortionState_deviceUpdate(ALdistortionState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
BiquadFilter_clear(&state->lowpass);
|
||||
BiquadFilter_clear(&state->bandpass);
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_update(ALdistortionState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat bandwidth;
|
||||
ALfloat cutoff;
|
||||
ALfloat edge;
|
||||
|
||||
/* Store waveshaper edge settings. */
|
||||
edge = sinf(props->Distortion.Edge * (F_PI_2));
|
||||
edge = minf(edge, 0.99f);
|
||||
state->edge_coeff = 2.0f * edge / (1.0f-edge);
|
||||
|
||||
cutoff = props->Distortion.LowpassCutoff;
|
||||
/* Bandwidth value is constant in octaves. */
|
||||
bandwidth = (cutoff / 2.0f) / (cutoff * 0.67f);
|
||||
/* Multiply sampling frequency by the amount of oversampling done during
|
||||
* processing.
|
||||
*/
|
||||
BiquadFilter_setParams(&state->lowpass, BiquadType_LowPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
cutoff = props->Distortion.EQCenter;
|
||||
/* Convert bandwidth in Hz to octaves. */
|
||||
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
|
||||
BiquadFilter_setParams(&state->bandpass, BiquadType_BandPass, 1.0f,
|
||||
cutoff / (frequency*4.0f), calc_rcpQ_from_bandwidth(cutoff / (frequency*4.0f), bandwidth)
|
||||
);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain*props->Distortion.Gain, state->Gain);
|
||||
}
|
||||
|
||||
static ALvoid ALdistortionState_process(ALdistortionState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat (*restrict buffer)[BUFFERSIZE] = state->Buffer;
|
||||
const ALfloat fc = state->edge_coeff;
|
||||
ALsizei base;
|
||||
ALsizei i, k;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
/* Perform 4x oversampling to avoid aliasing. Oversampling greatly
|
||||
* improves distortion quality and allows to implement lowpass and
|
||||
* bandpass filters using high frequencies, at which classic IIR
|
||||
* filters became unstable.
|
||||
*/
|
||||
ALsizei todo = mini(BUFFERSIZE, (SamplesToDo-base) * 4);
|
||||
|
||||
/* Fill oversample buffer using zero stuffing. Multiply the sample by
|
||||
* the amount of oversampling to maintain the signal's power.
|
||||
*/
|
||||
for(i = 0;i < todo;i++)
|
||||
buffer[0][i] = !(i&3) ? SamplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
|
||||
|
||||
/* First step, do lowpass filtering of original signal. Additionally
|
||||
* perform buffer interpolation and lowpass cutoff for oversampling
|
||||
* (which is fortunately first step of distortion). So combine three
|
||||
* operations into the one.
|
||||
*/
|
||||
BiquadFilter_process(&state->lowpass, buffer[1], buffer[0], todo);
|
||||
|
||||
/* Second step, do distortion using waveshaper function to emulate
|
||||
* signal processing during tube overdriving. Three steps of
|
||||
* waveshaping are intended to modify waveform without boost/clipping/
|
||||
* attenuation process.
|
||||
*/
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat smp = buffer[1][i];
|
||||
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
|
||||
smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
|
||||
|
||||
buffer[0][i] = smp;
|
||||
}
|
||||
|
||||
/* Third step, do bandpass filtering of distorted signal. */
|
||||
BiquadFilter_process(&state->bandpass, buffer[1], buffer[0], todo);
|
||||
|
||||
todo >>= 2;
|
||||
for(k = 0;k < NumChannels;k++)
|
||||
{
|
||||
/* Fourth step, final, do attenuation and perform decimation,
|
||||
* storing only one sample out of four.
|
||||
*/
|
||||
ALfloat gain = state->Gain[k];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
SamplesOut[k][base+i] += gain * buffer[1][i*4];
|
||||
}
|
||||
|
||||
base += todo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct DistortionStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} DistortionStateFactory;
|
||||
|
||||
static ALeffectState *DistortionStateFactory_create(DistortionStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALdistortionState *state;
|
||||
|
||||
NEW_OBJ0(state, ALdistortionState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(DistortionStateFactory);
|
||||
|
||||
|
||||
EffectStateFactory *DistortionStateFactory_getFactory(void)
|
||||
{
|
||||
static DistortionStateFactory DistortionFactory = { { GET_VTABLE2(DistortionStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &DistortionFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALdistortion_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
|
||||
void ALdistortion_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
|
||||
void ALdistortion_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
|
||||
props->Distortion.Edge = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
|
||||
props->Distortion.Gain = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
|
||||
props->Distortion.LowpassCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
|
||||
props->Distortion.EQCenter = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
|
||||
props->Distortion.EQBandwidth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALdistortion_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALdistortion_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALdistortion_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
|
||||
void ALdistortion_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
|
||||
void ALdistortion_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
*val = props->Distortion.Edge;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
*val = props->Distortion.Gain;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
*val = props->Distortion.LowpassCutoff;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
*val = props->Distortion.EQCenter;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
*val = props->Distortion.EQBandwidth;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid distortion float property 0x%04x",
|
||||
param);
|
||||
}
|
||||
}
|
||||
void ALdistortion_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALdistortion_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALdistortion);
|
||||
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alFilter.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
typedef struct ALechoState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
ALfloat *SampleBuffer;
|
||||
ALsizei BufferLength;
|
||||
|
||||
// The echo is two tap. The delay is the number of samples from before the
|
||||
// current offset
|
||||
struct {
|
||||
ALsizei delay;
|
||||
} Tap[2];
|
||||
ALsizei Offset;
|
||||
|
||||
/* The panning gains for the two taps */
|
||||
struct {
|
||||
ALfloat Current[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat Target[MAX_OUTPUT_CHANNELS];
|
||||
} Gains[2];
|
||||
|
||||
ALfloat FeedGain;
|
||||
|
||||
BiquadFilter Filter;
|
||||
} ALechoState;
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state);
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device);
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALechoState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALechoState);
|
||||
|
||||
|
||||
static void ALechoState_Construct(ALechoState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALechoState, ALeffectState, state);
|
||||
|
||||
state->BufferLength = 0;
|
||||
state->SampleBuffer = NULL;
|
||||
|
||||
state->Tap[0].delay = 0;
|
||||
state->Tap[1].delay = 0;
|
||||
state->Offset = 0;
|
||||
|
||||
BiquadFilter_clear(&state->Filter);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_Destruct(ALechoState *state)
|
||||
{
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = NULL;
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALechoState_deviceUpdate(ALechoState *state, ALCdevice *Device)
|
||||
{
|
||||
ALsizei maxlen;
|
||||
|
||||
// Use the next power of 2 for the buffer length, so the tap offsets can be
|
||||
// wrapped using a mask instead of a modulo
|
||||
maxlen = float2int(AL_ECHO_MAX_DELAY*Device->Frequency + 0.5f) +
|
||||
float2int(AL_ECHO_MAX_LRDELAY*Device->Frequency + 0.5f);
|
||||
maxlen = NextPowerOf2(maxlen);
|
||||
if(maxlen <= 0) return AL_FALSE;
|
||||
|
||||
if(maxlen != state->BufferLength)
|
||||
{
|
||||
void *temp = al_calloc(16, maxlen * sizeof(ALfloat));
|
||||
if(!temp) return AL_FALSE;
|
||||
|
||||
al_free(state->SampleBuffer);
|
||||
state->SampleBuffer = temp;
|
||||
state->BufferLength = maxlen;
|
||||
}
|
||||
|
||||
memset(state->SampleBuffer, 0, state->BufferLength*sizeof(ALfloat));
|
||||
memset(state->Gains, 0, sizeof(state->Gains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_update(ALechoState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALuint frequency = device->Frequency;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat gainhf, lrpan, spread;
|
||||
|
||||
state->Tap[0].delay = maxi(float2int(props->Echo.Delay*frequency + 0.5f), 1);
|
||||
state->Tap[1].delay = float2int(props->Echo.LRDelay*frequency + 0.5f);
|
||||
state->Tap[1].delay += state->Tap[0].delay;
|
||||
|
||||
spread = props->Echo.Spread;
|
||||
if(spread < 0.0f) lrpan = -1.0f;
|
||||
else lrpan = 1.0f;
|
||||
/* Convert echo spread (where 0 = omni, +/-1 = directional) to coverage
|
||||
* spread (where 0 = point, tau = omni).
|
||||
*/
|
||||
spread = asinf(1.0f - fabsf(spread))*4.0f;
|
||||
|
||||
state->FeedGain = props->Echo.Feedback;
|
||||
|
||||
gainhf = maxf(1.0f - props->Echo.Damping, 0.0625f); /* Limit -24dB */
|
||||
BiquadFilter_setParams(&state->Filter, BiquadType_HighShelf,
|
||||
gainhf, LOWPASSFREQREF/frequency, calc_rcpQ_from_slope(gainhf, 1.0f)
|
||||
);
|
||||
|
||||
/* First tap panning */
|
||||
CalcAngleCoeffs(-F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[0].Target);
|
||||
|
||||
/* Second tap panning */
|
||||
CalcAngleCoeffs( F_PI_2*lrpan, 0.0f, spread, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->Gains[1].Target);
|
||||
}
|
||||
|
||||
static ALvoid ALechoState_process(ALechoState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALsizei mask = state->BufferLength-1;
|
||||
const ALsizei tap1 = state->Tap[0].delay;
|
||||
const ALsizei tap2 = state->Tap[1].delay;
|
||||
ALfloat *restrict delaybuf = state->SampleBuffer;
|
||||
ALsizei offset = state->Offset;
|
||||
ALfloat z1, z2, in, out;
|
||||
ALsizei base;
|
||||
ALsizei c, i;
|
||||
|
||||
z1 = state->Filter.z1;
|
||||
z2 = state->Filter.z2;
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
alignas(16) ALfloat temps[2][128];
|
||||
ALsizei td = mini(128, SamplesToDo-base);
|
||||
|
||||
for(i = 0;i < td;i++)
|
||||
{
|
||||
/* Feed the delay buffer's input first. */
|
||||
delaybuf[offset&mask] = SamplesIn[0][i+base];
|
||||
|
||||
/* First tap */
|
||||
temps[0][i] = delaybuf[(offset-tap1) & mask];
|
||||
/* Second tap */
|
||||
temps[1][i] = delaybuf[(offset-tap2) & mask];
|
||||
|
||||
/* Apply damping to the second tap, then add it to the buffer with
|
||||
* feedback attenuation.
|
||||
*/
|
||||
in = temps[1][i];
|
||||
out = in*state->Filter.b0 + z1;
|
||||
z1 = in*state->Filter.b1 - out*state->Filter.a1 + z2;
|
||||
z2 = in*state->Filter.b2 - out*state->Filter.a2;
|
||||
|
||||
delaybuf[offset&mask] += out * state->FeedGain;
|
||||
offset++;
|
||||
}
|
||||
|
||||
for(c = 0;c < 2;c++)
|
||||
MixSamples(temps[c], NumChannels, SamplesOut, state->Gains[c].Current,
|
||||
state->Gains[c].Target, SamplesToDo-base, base, td);
|
||||
|
||||
base += td;
|
||||
}
|
||||
state->Filter.z1 = z1;
|
||||
state->Filter.z2 = z2;
|
||||
|
||||
state->Offset = offset;
|
||||
}
|
||||
|
||||
|
||||
typedef struct EchoStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} EchoStateFactory;
|
||||
|
||||
ALeffectState *EchoStateFactory_create(EchoStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALechoState *state;
|
||||
|
||||
NEW_OBJ0(state, ALechoState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(EchoStateFactory);
|
||||
|
||||
EffectStateFactory *EchoStateFactory_getFactory(void)
|
||||
{
|
||||
static EchoStateFactory EchoFactory = { { GET_VTABLE2(EchoStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &EchoFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALecho_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
|
||||
void ALecho_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
|
||||
void ALecho_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo delay out of range");
|
||||
props->Echo.Delay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo LR delay out of range");
|
||||
props->Echo.LRDelay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo damping out of range");
|
||||
props->Echo.Damping = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo feedback out of range");
|
||||
props->Echo.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Echo spread out of range");
|
||||
props->Echo.Spread = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALecho_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALecho_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALecho_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param); }
|
||||
void ALecho_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param); }
|
||||
void ALecho_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
*val = props->Echo.Delay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
*val = props->Echo.LRDelay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
*val = props->Echo.Damping;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
*val = props->Echo.Feedback;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
*val = props->Echo.Spread;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALecho_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALecho_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALecho);
|
||||
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2013 by Mike Gorchak
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
/* The document "Effects Extension Guide.pdf" says that low and high *
|
||||
* frequencies are cutoff frequencies. This is not fully correct, they *
|
||||
* are corner frequencies for low and high shelf filters. If they were *
|
||||
* just cutoff frequencies, there would be no need in cutoff frequency *
|
||||
* gains, which are present. Documentation for "Creative Proteus X2" *
|
||||
* software describes 4-band equalizer functionality in a much better *
|
||||
* way. This equalizer seems to be a predecessor of OpenAL 4-band *
|
||||
* equalizer. With low and high shelf filters we are able to cutoff *
|
||||
* frequencies below and/or above corner frequencies using attenuation *
|
||||
* gains (below 1.0) and amplify all low and/or high frequencies using *
|
||||
* gains above 1.0. *
|
||||
* *
|
||||
* Low-shelf Low Mid Band High Mid Band High-shelf *
|
||||
* corner center center corner *
|
||||
* frequency frequency frequency frequency *
|
||||
* 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
|
||||
* *
|
||||
* | | | | *
|
||||
* | | | | *
|
||||
* B -----+ /--+--\ /--+--\ +----- *
|
||||
* O |\ | | | | | | /| *
|
||||
* O | \ - | - - | - / | *
|
||||
* S + | \ | | | | | | / | *
|
||||
* T | | | | | | | | | | *
|
||||
* ---------+---------------+------------------+---------------+-------- *
|
||||
* C | | | | | | | | | | *
|
||||
* U - | / | | | | | | \ | *
|
||||
* T | / - | - - | - \ | *
|
||||
* O |/ | | | | | | \| *
|
||||
* F -----+ \--+--/ \--+--/ +----- *
|
||||
* F | | | | *
|
||||
* | | | | *
|
||||
* *
|
||||
* Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
|
||||
* up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
|
||||
* octaves for two mid bands. *
|
||||
* *
|
||||
* Implementation is based on the "Cookbook formulae for audio EQ biquad *
|
||||
* filter coefficients" by Robert Bristow-Johnson *
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
|
||||
|
||||
|
||||
typedef struct ALequalizerState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
struct {
|
||||
/* Effect parameters */
|
||||
BiquadFilter filter[4];
|
||||
|
||||
/* Effect gains for each channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
|
||||
ALfloat SampleBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALequalizerState;
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state);
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *device);
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALequalizerState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALequalizerState);
|
||||
|
||||
|
||||
static void ALequalizerState_Construct(ALequalizerState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALequalizerState, ALeffectState, state);
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_Destruct(ALequalizerState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALequalizerState_deviceUpdate(ALequalizerState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
ALsizei i, j;
|
||||
|
||||
for(i = 0; i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
for(j = 0;j < 4;j++)
|
||||
BiquadFilter_clear(&state->Chans[i].filter[j]);
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_update(ALequalizerState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat frequency = (ALfloat)device->Frequency;
|
||||
ALfloat gain, f0norm;
|
||||
ALuint i;
|
||||
|
||||
/* Calculate coefficients for the each type of filter. Note that the shelf
|
||||
* filters' gain is for the reference frequency, which is the centerpoint
|
||||
* of the transition band.
|
||||
*/
|
||||
gain = maxf(sqrtf(props->Equalizer.LowGain), 0.0625f); /* Limit -24dB */
|
||||
f0norm = props->Equalizer.LowCutoff/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[0], BiquadType_LowShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid1Gain, 0.0625f);
|
||||
f0norm = props->Equalizer.Mid1Center/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[1], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid1Width
|
||||
)
|
||||
);
|
||||
|
||||
gain = maxf(props->Equalizer.Mid2Gain, 0.0625f);
|
||||
f0norm = props->Equalizer.Mid2Center/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[2], BiquadType_Peaking,
|
||||
gain, f0norm, calc_rcpQ_from_bandwidth(
|
||||
f0norm, props->Equalizer.Mid2Width
|
||||
)
|
||||
);
|
||||
|
||||
gain = maxf(sqrtf(props->Equalizer.HighGain), 0.0625f);
|
||||
f0norm = props->Equalizer.HighCutoff/frequency;
|
||||
BiquadFilter_setParams(&state->Chans[0].filter[3], BiquadType_HighShelf,
|
||||
gain, f0norm, calc_rcpQ_from_slope(gain, 0.75f)
|
||||
);
|
||||
|
||||
/* Copy the filter coefficients for the other input channels. */
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
BiquadFilter_copyParams(&state->Chans[i].filter[0], &state->Chans[0].filter[0]);
|
||||
BiquadFilter_copyParams(&state->Chans[i].filter[1], &state->Chans[0].filter[1]);
|
||||
BiquadFilter_copyParams(&state->Chans[i].filter[2], &state->Chans[0].filter[2]);
|
||||
BiquadFilter_copyParams(&state->Chans[i].filter[3], &state->Chans[0].filter[3]);
|
||||
}
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALequalizerState_process(ALequalizerState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
ALfloat (*restrict temps)[BUFFERSIZE] = state->SampleBuffer;
|
||||
ALsizei c;
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
BiquadFilter_process(&state->Chans[c].filter[0], temps[0], SamplesIn[c], SamplesToDo);
|
||||
BiquadFilter_process(&state->Chans[c].filter[1], temps[1], temps[0], SamplesToDo);
|
||||
BiquadFilter_process(&state->Chans[c].filter[2], temps[2], temps[1], SamplesToDo);
|
||||
BiquadFilter_process(&state->Chans[c].filter[3], temps[3], temps[2], SamplesToDo);
|
||||
|
||||
MixSamples(temps[3], NumChannels, SamplesOut,
|
||||
state->Chans[c].CurrentGains, state->Chans[c].TargetGains,
|
||||
SamplesToDo, 0, SamplesToDo
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct EqualizerStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} EqualizerStateFactory;
|
||||
|
||||
ALeffectState *EqualizerStateFactory_create(EqualizerStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALequalizerState *state;
|
||||
|
||||
NEW_OBJ0(state, ALequalizerState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(EqualizerStateFactory);
|
||||
|
||||
EffectStateFactory *EqualizerStateFactory_getFactory(void)
|
||||
{
|
||||
static EqualizerStateFactory EqualizerFactory = { { GET_VTABLE2(EqualizerStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &EqualizerFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALequalizer_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
|
||||
void ALequalizer_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
|
||||
void ALequalizer_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band gain out of range");
|
||||
props->Equalizer.LowGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer low-band cutoff out of range");
|
||||
props->Equalizer.LowCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band gain out of range");
|
||||
props->Equalizer.Mid1Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band center out of range");
|
||||
props->Equalizer.Mid1Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid1-band width out of range");
|
||||
props->Equalizer.Mid1Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band gain out of range");
|
||||
props->Equalizer.Mid2Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band center out of range");
|
||||
props->Equalizer.Mid2Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer mid2-band width out of range");
|
||||
props->Equalizer.Mid2Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band gain out of range");
|
||||
props->Equalizer.HighGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Equalizer high-band cutoff out of range");
|
||||
props->Equalizer.HighCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALequalizer_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALequalizer_setParamf(effect, context, param, vals[0]); }
|
||||
|
||||
void ALequalizer_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param); }
|
||||
void ALequalizer_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
|
||||
{ alSetError(context, AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", param); }
|
||||
void ALequalizer_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
*val = props->Equalizer.LowGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
*val = props->Equalizer.LowCutoff;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
*val = props->Equalizer.Mid1Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
*val = props->Equalizer.Mid1Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
*val = props->Equalizer.Mid1Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
*val = props->Equalizer.Mid2Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
*val = props->Equalizer.Mid2Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
*val = props->Equalizer.Mid2Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
*val = props->Equalizer.HighGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
*val = props->Equalizer.HighCutoff;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALequalizer_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALequalizer_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALequalizer);
|
||||
@@ -0,0 +1,329 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2018 by Raul Herraiz.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
#include "alcomplex.h"
|
||||
|
||||
#define HIL_SIZE 1024
|
||||
#define OVERSAMP (1<<2)
|
||||
|
||||
#define HIL_STEP (HIL_SIZE / OVERSAMP)
|
||||
#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))
|
||||
|
||||
|
||||
typedef struct ALfshifterState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect parameters */
|
||||
ALsizei count;
|
||||
ALsizei PhaseStep;
|
||||
ALsizei Phase;
|
||||
ALdouble ld_sign;
|
||||
|
||||
/*Effects buffers*/
|
||||
ALfloat InFIFO[HIL_SIZE];
|
||||
ALcomplex OutFIFO[HIL_SIZE];
|
||||
ALcomplex OutputAccum[HIL_SIZE];
|
||||
ALcomplex Analytic[HIL_SIZE];
|
||||
ALcomplex Outdata[BUFFERSIZE];
|
||||
|
||||
alignas(16) ALfloat BufferOut[BUFFERSIZE];
|
||||
|
||||
/* Effect gains for each output channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} ALfshifterState;
|
||||
|
||||
static ALvoid ALfshifterState_Destruct(ALfshifterState *state);
|
||||
static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *device);
|
||||
static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALfshifterState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALfshifterState);
|
||||
|
||||
/* Define a Hann window, used to filter the HIL input and output. */
|
||||
alignas(16) static ALdouble HannWindow[HIL_SIZE];
|
||||
|
||||
static void InitHannWindow(void)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
/* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
|
||||
for(i = 0;i < HIL_SIZE>>1;i++)
|
||||
{
|
||||
ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(HIL_SIZE-1));
|
||||
HannWindow[i] = HannWindow[HIL_SIZE-1-i] = val * val;
|
||||
}
|
||||
}
|
||||
|
||||
static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
|
||||
|
||||
static void ALfshifterState_Construct(ALfshifterState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALfshifterState, ALeffectState, state);
|
||||
|
||||
alcall_once(&HannInitOnce, InitHannWindow);
|
||||
}
|
||||
|
||||
static ALvoid ALfshifterState_Destruct(ALfshifterState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALfshifterState_deviceUpdate(ALfshifterState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
state->count = FIFO_LATENCY;
|
||||
state->PhaseStep = 0;
|
||||
state->Phase = 0;
|
||||
state->ld_sign = 1.0;
|
||||
|
||||
memset(state->InFIFO, 0, sizeof(state->InFIFO));
|
||||
memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
|
||||
memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
|
||||
memset(state->Analytic, 0, sizeof(state->Analytic));
|
||||
|
||||
memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
|
||||
memset(state->TargetGains, 0, sizeof(state->TargetGains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALfshifterState_update(ALfshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
ALfloat step;
|
||||
|
||||
step = props->Fshifter.Frequency / (ALfloat)device->Frequency;
|
||||
state->PhaseStep = fastf2i(minf(step, 0.5f) * FRACTIONONE);
|
||||
|
||||
switch(props->Fshifter.LeftDirection)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
|
||||
state->ld_sign = -1.0;
|
||||
break;
|
||||
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
|
||||
state->ld_sign = 1.0;
|
||||
break;
|
||||
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
|
||||
state->Phase = 0;
|
||||
state->PhaseStep = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALfshifterState_process(ALfshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
static const ALcomplex complex_zero = { 0.0, 0.0 };
|
||||
ALfloat *restrict BufferOut = state->BufferOut;
|
||||
ALsizei j, k, base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALsizei todo = mini(HIL_SIZE-state->count, SamplesToDo-base);
|
||||
|
||||
ASSUME(todo > 0);
|
||||
|
||||
/* Fill FIFO buffer with samples data */
|
||||
k = state->count;
|
||||
for(j = 0;j < todo;j++,k++)
|
||||
{
|
||||
state->InFIFO[k] = SamplesIn[0][base+j];
|
||||
state->Outdata[base+j] = state->OutFIFO[k-FIFO_LATENCY];
|
||||
}
|
||||
state->count += todo;
|
||||
base += todo;
|
||||
|
||||
/* Check whether FIFO buffer is filled */
|
||||
if(state->count < HIL_SIZE) continue;
|
||||
|
||||
state->count = FIFO_LATENCY;
|
||||
|
||||
/* Real signal windowing and store in Analytic buffer */
|
||||
for(k = 0;k < HIL_SIZE;k++)
|
||||
{
|
||||
state->Analytic[k].Real = state->InFIFO[k] * HannWindow[k];
|
||||
state->Analytic[k].Imag = 0.0;
|
||||
}
|
||||
|
||||
/* Processing signal by Discrete Hilbert Transform (analytical signal). */
|
||||
complex_hilbert(state->Analytic, HIL_SIZE);
|
||||
|
||||
/* Windowing and add to output accumulator */
|
||||
for(k = 0;k < HIL_SIZE;k++)
|
||||
{
|
||||
state->OutputAccum[k].Real += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Real;
|
||||
state->OutputAccum[k].Imag += 2.0/OVERSAMP*HannWindow[k]*state->Analytic[k].Imag;
|
||||
}
|
||||
|
||||
/* Shift accumulator, input & output FIFO */
|
||||
for(k = 0;k < HIL_STEP;k++) state->OutFIFO[k] = state->OutputAccum[k];
|
||||
for(j = 0;k < HIL_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
|
||||
for(;j < HIL_SIZE;j++) state->OutputAccum[j] = complex_zero;
|
||||
for(k = 0;k < FIFO_LATENCY;k++)
|
||||
state->InFIFO[k] = state->InFIFO[k+HIL_STEP];
|
||||
}
|
||||
|
||||
/* Process frequency shifter using the analytic signal obtained. */
|
||||
for(k = 0;k < SamplesToDo;k++)
|
||||
{
|
||||
ALdouble phase = state->Phase * ((1.0/FRACTIONONE) * 2.0*M_PI);
|
||||
BufferOut[k] = (ALfloat)(state->Outdata[k].Real*cos(phase) +
|
||||
state->Outdata[k].Imag*sin(phase)*state->ld_sign);
|
||||
|
||||
state->Phase += state->PhaseStep;
|
||||
state->Phase &= FRACTIONMASK;
|
||||
}
|
||||
|
||||
/* Now, mix the processed sound data to the output. */
|
||||
MixSamples(BufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
|
||||
maxi(SamplesToDo, 512), 0, SamplesToDo);
|
||||
}
|
||||
|
||||
typedef struct FshifterStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} FshifterStateFactory;
|
||||
|
||||
static ALeffectState *FshifterStateFactory_create(FshifterStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALfshifterState *state;
|
||||
|
||||
NEW_OBJ0(state, ALfshifterState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(FshifterStateFactory);
|
||||
|
||||
EffectStateFactory *FshifterStateFactory_getFactory(void)
|
||||
{
|
||||
static FshifterStateFactory FshifterFactory = { { GET_VTABLE2(FshifterStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &FshifterFactory);
|
||||
}
|
||||
|
||||
void ALfshifter_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
|
||||
props->Fshifter.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
|
||||
void ALfshifter_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{
|
||||
ALfshifter_setParamf(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALfshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
|
||||
props->Fshifter.LeftDirection = val;
|
||||
break;
|
||||
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
|
||||
props->Fshifter.RightDirection = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALfshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALfshifter_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALfshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
*val = props->Fshifter.LeftDirection;
|
||||
break;
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
*val = props->Fshifter.RightDirection;
|
||||
break;
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALfshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALfshifter_getParami(effect, context, param, vals);
|
||||
}
|
||||
|
||||
void ALfshifter_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
*val = props->Fshifter.Frequency;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", param);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ALfshifter_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{
|
||||
ALfshifter_getParamf(effect, context, param, vals);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALfshifter);
|
||||
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2009 by Chris Robinson.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
|
||||
#define MAX_UPDATE_SAMPLES 128
|
||||
|
||||
typedef struct ALmodulatorState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
void (*GetSamples)(ALfloat*, ALsizei, const ALsizei, ALsizei);
|
||||
|
||||
ALsizei index;
|
||||
ALsizei step;
|
||||
|
||||
struct {
|
||||
BiquadFilter Filter;
|
||||
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} Chans[MAX_EFFECT_CHANNELS];
|
||||
} ALmodulatorState;
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state);
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *device);
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALmodulatorState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALmodulatorState);
|
||||
|
||||
|
||||
#define WAVEFORM_FRACBITS 24
|
||||
#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
|
||||
#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
|
||||
|
||||
static inline ALfloat Sin(ALsizei index)
|
||||
{
|
||||
return sinf((ALfloat)index * (F_TAU / WAVEFORM_FRACONE));
|
||||
}
|
||||
|
||||
static inline ALfloat Saw(ALsizei index)
|
||||
{
|
||||
return (ALfloat)index*(2.0f/WAVEFORM_FRACONE) - 1.0f;
|
||||
}
|
||||
|
||||
static inline ALfloat Square(ALsizei index)
|
||||
{
|
||||
return (ALfloat)(((index>>(WAVEFORM_FRACBITS-2))&2) - 1);
|
||||
}
|
||||
|
||||
static inline ALfloat One(ALsizei UNUSED(index))
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(func) \
|
||||
static void Modulate##func(ALfloat *restrict dst, ALsizei index, \
|
||||
const ALsizei step, ALsizei todo) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < todo;i++) \
|
||||
{ \
|
||||
index += step; \
|
||||
index &= WAVEFORM_FRACMASK; \
|
||||
dst[i] = func(index); \
|
||||
} \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(Sin)
|
||||
DECL_TEMPLATE(Saw)
|
||||
DECL_TEMPLATE(Square)
|
||||
DECL_TEMPLATE(One)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
static void ALmodulatorState_Construct(ALmodulatorState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALmodulatorState, ALeffectState, state);
|
||||
|
||||
state->index = 0;
|
||||
state->step = 1;
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_Destruct(ALmodulatorState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALmodulatorState_deviceUpdate(ALmodulatorState *state, ALCdevice *UNUSED(device))
|
||||
{
|
||||
ALsizei i, j;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
{
|
||||
BiquadFilter_clear(&state->Chans[i].Filter);
|
||||
for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
|
||||
state->Chans[i].CurrentGains[j] = 0.0f;
|
||||
}
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_update(ALmodulatorState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat f0norm;
|
||||
ALsizei i;
|
||||
|
||||
state->step = fastf2i(props->Modulator.Frequency / (ALfloat)device->Frequency *
|
||||
WAVEFORM_FRACONE);
|
||||
state->step = clampi(state->step, 0, WAVEFORM_FRACONE-1);
|
||||
|
||||
if(state->step == 0)
|
||||
state->GetSamples = ModulateOne;
|
||||
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SINUSOID)
|
||||
state->GetSamples = ModulateSin;
|
||||
else if(props->Modulator.Waveform == AL_RING_MODULATOR_SAWTOOTH)
|
||||
state->GetSamples = ModulateSaw;
|
||||
else /*if(Slot->Params.EffectProps.Modulator.Waveform == AL_RING_MODULATOR_SQUARE)*/
|
||||
state->GetSamples = ModulateSquare;
|
||||
|
||||
f0norm = props->Modulator.HighPassCutoff / (ALfloat)device->Frequency;
|
||||
f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
|
||||
/* Bandwidth value is constant in octaves. */
|
||||
BiquadFilter_setParams(&state->Chans[0].Filter, BiquadType_HighPass, 1.0f,
|
||||
f0norm, calc_rcpQ_from_bandwidth(f0norm, 0.75f));
|
||||
for(i = 1;i < MAX_EFFECT_CHANNELS;i++)
|
||||
BiquadFilter_copyParams(&state->Chans[i].Filter, &state->Chans[0].Filter);
|
||||
|
||||
STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
|
||||
STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
|
||||
for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
|
||||
ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
|
||||
state->Chans[i].TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALmodulatorState_process(ALmodulatorState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
const ALsizei step = state->step;
|
||||
ALsizei base;
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
alignas(16) ALfloat modsamples[MAX_UPDATE_SAMPLES];
|
||||
ALsizei td = mini(MAX_UPDATE_SAMPLES, SamplesToDo-base);
|
||||
ALsizei c, i;
|
||||
|
||||
state->GetSamples(modsamples, state->index, step, td);
|
||||
state->index += (step*td) & WAVEFORM_FRACMASK;
|
||||
state->index &= WAVEFORM_FRACMASK;
|
||||
|
||||
for(c = 0;c < MAX_EFFECT_CHANNELS;c++)
|
||||
{
|
||||
alignas(16) ALfloat temps[MAX_UPDATE_SAMPLES];
|
||||
|
||||
BiquadFilter_process(&state->Chans[c].Filter, temps, &SamplesIn[c][base], td);
|
||||
for(i = 0;i < td;i++)
|
||||
temps[i] *= modsamples[i];
|
||||
|
||||
MixSamples(temps, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
|
||||
state->Chans[c].TargetGains, SamplesToDo-base, base, td);
|
||||
}
|
||||
|
||||
base += td;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct ModulatorStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} ModulatorStateFactory;
|
||||
|
||||
static ALeffectState *ModulatorStateFactory_create(ModulatorStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALmodulatorState *state;
|
||||
|
||||
NEW_OBJ0(state, ALmodulatorState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(ModulatorStateFactory);
|
||||
|
||||
EffectStateFactory *ModulatorStateFactory_getFactory(void)
|
||||
{
|
||||
static ModulatorStateFactory ModulatorFactory = { { GET_VTABLE2(ModulatorStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &ModulatorFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALmodulator_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator frequency out of range");
|
||||
props->Modulator.Frequency = val;
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Modulator high-pass cutoff out of range");
|
||||
props->Modulator.HighPassCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
|
||||
{ ALmodulator_setParamf(effect, context, param, vals[0]); }
|
||||
void ALmodulator_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
ALmodulator_setParamf(effect, context, param, (ALfloat)val);
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_WAVEFORM && val <= AL_RING_MODULATOR_MAX_WAVEFORM))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid modulator waveform");
|
||||
props->Modulator.Waveform = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{ ALmodulator_setParami(effect, context, param, vals[0]); }
|
||||
|
||||
void ALmodulator_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = (ALint)props->Modulator.Frequency;
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = (ALint)props->Modulator.HighPassCutoff;
|
||||
break;
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
*val = props->Modulator.Waveform;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{ ALmodulator_getParami(effect, context, param, vals); }
|
||||
void ALmodulator_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = props->Modulator.Frequency;
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = props->Modulator.HighPassCutoff;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALmodulator_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
|
||||
{ ALmodulator_getParamf(effect, context, param, vals); }
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALmodulator);
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
|
||||
|
||||
typedef struct ALnullState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
} ALnullState;
|
||||
|
||||
/* Forward-declare "virtual" functions to define the vtable with. */
|
||||
static ALvoid ALnullState_Destruct(ALnullState *state);
|
||||
static ALboolean ALnullState_deviceUpdate(ALnullState *state, ALCdevice *device);
|
||||
static ALvoid ALnullState_update(ALnullState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALnullState_process(ALnullState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei mumChannels);
|
||||
static void *ALnullState_New(size_t size);
|
||||
static void ALnullState_Delete(void *ptr);
|
||||
|
||||
/* Define the ALeffectState vtable for this type. */
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALnullState);
|
||||
|
||||
|
||||
/* This constructs the effect state. It's called when the object is first
|
||||
* created. Make sure to call the parent Construct function first, and set the
|
||||
* vtable!
|
||||
*/
|
||||
static void ALnullState_Construct(ALnullState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALnullState, ALeffectState, state);
|
||||
}
|
||||
|
||||
/* This destructs (not free!) the effect state. It's called only when the
|
||||
* effect slot is no longer used. Make sure to call the parent Destruct
|
||||
* function before returning!
|
||||
*/
|
||||
static ALvoid ALnullState_Destruct(ALnullState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
/* This updates the device-dependant effect state. This is called on
|
||||
* initialization and any time the device parameters (eg. playback frequency,
|
||||
* format) have been changed.
|
||||
*/
|
||||
static ALboolean ALnullState_deviceUpdate(ALnullState* UNUSED(state), ALCdevice* UNUSED(device))
|
||||
{
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
/* This updates the effect state. This is called any time the effect is
|
||||
* (re)loaded into a slot.
|
||||
*/
|
||||
static ALvoid ALnullState_update(ALnullState* UNUSED(state), const ALCcontext* UNUSED(context), const ALeffectslot* UNUSED(slot), const ALeffectProps* UNUSED(props))
|
||||
{
|
||||
}
|
||||
|
||||
/* This processes the effect state, for the given number of samples from the
|
||||
* input to the output buffer. The result should be added to the output buffer,
|
||||
* not replace it.
|
||||
*/
|
||||
static ALvoid ALnullState_process(ALnullState* UNUSED(state), ALsizei UNUSED(samplesToDo), const ALfloatBUFFERSIZE*restrict UNUSED(samplesIn), ALfloatBUFFERSIZE*restrict UNUSED(samplesOut), ALsizei UNUSED(numChannels))
|
||||
{
|
||||
}
|
||||
|
||||
/* This allocates memory to store the object, before it gets constructed.
|
||||
* DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
|
||||
*/
|
||||
static void *ALnullState_New(size_t size)
|
||||
{
|
||||
return al_malloc(16, size);
|
||||
}
|
||||
|
||||
/* This frees the memory used by the object, after it has been destructed.
|
||||
* DECLARE_DEFAULT_ALLOCATORS can be used to declare a default method.
|
||||
*/
|
||||
static void ALnullState_Delete(void *ptr)
|
||||
{
|
||||
al_free(ptr);
|
||||
}
|
||||
|
||||
|
||||
typedef struct NullStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} NullStateFactory;
|
||||
|
||||
/* Creates ALeffectState objects of the appropriate type. */
|
||||
ALeffectState *NullStateFactory_create(NullStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALnullState *state;
|
||||
|
||||
NEW_OBJ0(state, ALnullState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
/* Define the EffectStateFactory vtable for this type. */
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(NullStateFactory);
|
||||
|
||||
EffectStateFactory *NullStateFactory_getFactory(void)
|
||||
{
|
||||
static NullStateFactory NullFactory = { { GET_VTABLE2(NullStateFactory, EffectStateFactory) } };
|
||||
return STATIC_CAST(EffectStateFactory, &NullFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALnull_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
|
||||
void ALnull_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect integer-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(val))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALnull_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat* UNUSED(vals))
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid null effect float-vector property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALnull);
|
||||
@@ -0,0 +1,441 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2018 by Raul Herraiz.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alError.h"
|
||||
#include "alu.h"
|
||||
#include "filters/defs.h"
|
||||
|
||||
#include "alcomplex.h"
|
||||
|
||||
|
||||
#define STFT_SIZE 1024
|
||||
#define STFT_HALF_SIZE (STFT_SIZE>>1)
|
||||
#define OVERSAMP (1<<2)
|
||||
|
||||
#define STFT_STEP (STFT_SIZE / OVERSAMP)
|
||||
#define FIFO_LATENCY (STFT_STEP * (OVERSAMP-1))
|
||||
|
||||
|
||||
typedef struct ALphasor {
|
||||
ALdouble Amplitude;
|
||||
ALdouble Phase;
|
||||
} ALphasor;
|
||||
|
||||
typedef struct ALFrequencyDomain {
|
||||
ALdouble Amplitude;
|
||||
ALdouble Frequency;
|
||||
} ALfrequencyDomain;
|
||||
|
||||
|
||||
typedef struct ALpshifterState {
|
||||
DERIVE_FROM_TYPE(ALeffectState);
|
||||
|
||||
/* Effect parameters */
|
||||
ALsizei count;
|
||||
ALsizei PitchShiftI;
|
||||
ALfloat PitchShift;
|
||||
ALfloat FreqPerBin;
|
||||
|
||||
/*Effects buffers*/
|
||||
ALfloat InFIFO[STFT_SIZE];
|
||||
ALfloat OutFIFO[STFT_STEP];
|
||||
ALdouble LastPhase[STFT_HALF_SIZE+1];
|
||||
ALdouble SumPhase[STFT_HALF_SIZE+1];
|
||||
ALdouble OutputAccum[STFT_SIZE];
|
||||
|
||||
ALcomplex FFTbuffer[STFT_SIZE];
|
||||
|
||||
ALfrequencyDomain Analysis_buffer[STFT_HALF_SIZE+1];
|
||||
ALfrequencyDomain Syntesis_buffer[STFT_HALF_SIZE+1];
|
||||
|
||||
alignas(16) ALfloat BufferOut[BUFFERSIZE];
|
||||
|
||||
/* Effect gains for each output channel */
|
||||
ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
|
||||
} ALpshifterState;
|
||||
|
||||
static ALvoid ALpshifterState_Destruct(ALpshifterState *state);
|
||||
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device);
|
||||
static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
|
||||
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
|
||||
DECLARE_DEFAULT_ALLOCATORS(ALpshifterState)
|
||||
|
||||
DEFINE_ALEFFECTSTATE_VTABLE(ALpshifterState);
|
||||
|
||||
|
||||
/* Define a Hann window, used to filter the STFT input and output. */
|
||||
alignas(16) static ALdouble HannWindow[STFT_SIZE];
|
||||
|
||||
static void InitHannWindow(void)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
/* Create lookup table of the Hann window for the desired size, i.e. STFT_SIZE */
|
||||
for(i = 0;i < STFT_SIZE>>1;i++)
|
||||
{
|
||||
ALdouble val = sin(M_PI * (ALdouble)i / (ALdouble)(STFT_SIZE-1));
|
||||
HannWindow[i] = HannWindow[STFT_SIZE-1-i] = val * val;
|
||||
}
|
||||
}
|
||||
static alonce_flag HannInitOnce = AL_ONCE_FLAG_INIT;
|
||||
|
||||
|
||||
static inline ALint double2int(ALdouble d)
|
||||
{
|
||||
#if ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) && \
|
||||
!defined(__SSE2_MATH__)) || (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2)
|
||||
ALint sign, shift;
|
||||
ALint64 mant;
|
||||
union {
|
||||
ALdouble d;
|
||||
ALint64 i64;
|
||||
} conv;
|
||||
|
||||
conv.d = d;
|
||||
sign = (conv.i64>>63) | 1;
|
||||
shift = ((conv.i64>>52)&0x7ff) - (1023+52);
|
||||
|
||||
/* Over/underflow */
|
||||
if(UNLIKELY(shift >= 63 || shift < -52))
|
||||
return 0;
|
||||
|
||||
mant = (conv.i64&I64(0xfffffffffffff)) | I64(0x10000000000000);
|
||||
if(LIKELY(shift < 0))
|
||||
return (ALint)(mant >> -shift) * sign;
|
||||
return (ALint)(mant << shift) * sign;
|
||||
|
||||
#else
|
||||
|
||||
return (ALint)d;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Converts ALcomplex to ALphasor */
|
||||
static inline ALphasor rect2polar(ALcomplex number)
|
||||
{
|
||||
ALphasor polar;
|
||||
|
||||
polar.Amplitude = sqrt(number.Real*number.Real + number.Imag*number.Imag);
|
||||
polar.Phase = atan2(number.Imag, number.Real);
|
||||
|
||||
return polar;
|
||||
}
|
||||
|
||||
/* Converts ALphasor to ALcomplex */
|
||||
static inline ALcomplex polar2rect(ALphasor number)
|
||||
{
|
||||
ALcomplex cartesian;
|
||||
|
||||
cartesian.Real = number.Amplitude * cos(number.Phase);
|
||||
cartesian.Imag = number.Amplitude * sin(number.Phase);
|
||||
|
||||
return cartesian;
|
||||
}
|
||||
|
||||
|
||||
static void ALpshifterState_Construct(ALpshifterState *state)
|
||||
{
|
||||
ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
|
||||
SET_VTABLE2(ALpshifterState, ALeffectState, state);
|
||||
|
||||
alcall_once(&HannInitOnce, InitHannWindow);
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_Destruct(ALpshifterState *state)
|
||||
{
|
||||
ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
|
||||
}
|
||||
|
||||
static ALboolean ALpshifterState_deviceUpdate(ALpshifterState *state, ALCdevice *device)
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
state->count = FIFO_LATENCY;
|
||||
state->PitchShiftI = FRACTIONONE;
|
||||
state->PitchShift = 1.0f;
|
||||
state->FreqPerBin = device->Frequency / (ALfloat)STFT_SIZE;
|
||||
|
||||
memset(state->InFIFO, 0, sizeof(state->InFIFO));
|
||||
memset(state->OutFIFO, 0, sizeof(state->OutFIFO));
|
||||
memset(state->FFTbuffer, 0, sizeof(state->FFTbuffer));
|
||||
memset(state->LastPhase, 0, sizeof(state->LastPhase));
|
||||
memset(state->SumPhase, 0, sizeof(state->SumPhase));
|
||||
memset(state->OutputAccum, 0, sizeof(state->OutputAccum));
|
||||
memset(state->Analysis_buffer, 0, sizeof(state->Analysis_buffer));
|
||||
memset(state->Syntesis_buffer, 0, sizeof(state->Syntesis_buffer));
|
||||
|
||||
memset(state->CurrentGains, 0, sizeof(state->CurrentGains));
|
||||
memset(state->TargetGains, 0, sizeof(state->TargetGains));
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_update(ALpshifterState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
|
||||
{
|
||||
const ALCdevice *device = context->Device;
|
||||
ALfloat coeffs[MAX_AMBI_COEFFS];
|
||||
float pitch;
|
||||
|
||||
pitch = powf(2.0f,
|
||||
(ALfloat)(props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune) / 1200.0f
|
||||
);
|
||||
state->PitchShiftI = fastf2i(pitch*FRACTIONONE);
|
||||
state->PitchShift = state->PitchShiftI * (1.0f/FRACTIONONE);
|
||||
|
||||
CalcAngleCoeffs(0.0f, 0.0f, 0.0f, coeffs);
|
||||
ComputePanGains(&device->Dry, coeffs, slot->Params.Gain, state->TargetGains);
|
||||
}
|
||||
|
||||
static ALvoid ALpshifterState_process(ALpshifterState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
|
||||
{
|
||||
/* Pitch shifter engine based on the work of Stephan Bernsee.
|
||||
* http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
|
||||
*/
|
||||
|
||||
static const ALdouble expected = M_PI*2.0 / OVERSAMP;
|
||||
const ALdouble freq_per_bin = state->FreqPerBin;
|
||||
ALfloat *restrict bufferOut = state->BufferOut;
|
||||
ALsizei count = state->count;
|
||||
ALsizei i, j, k;
|
||||
|
||||
for(i = 0;i < SamplesToDo;)
|
||||
{
|
||||
do {
|
||||
/* Fill FIFO buffer with samples data */
|
||||
state->InFIFO[count] = SamplesIn[0][i];
|
||||
bufferOut[i] = state->OutFIFO[count - FIFO_LATENCY];
|
||||
|
||||
count++;
|
||||
} while(++i < SamplesToDo && count < STFT_SIZE);
|
||||
|
||||
/* Check whether FIFO buffer is filled */
|
||||
if(count < STFT_SIZE) break;
|
||||
count = FIFO_LATENCY;
|
||||
|
||||
/* Real signal windowing and store in FFTbuffer */
|
||||
for(k = 0;k < STFT_SIZE;k++)
|
||||
{
|
||||
state->FFTbuffer[k].Real = state->InFIFO[k] * HannWindow[k];
|
||||
state->FFTbuffer[k].Imag = 0.0;
|
||||
}
|
||||
|
||||
/* ANALYSIS */
|
||||
/* Apply FFT to FFTbuffer data */
|
||||
complex_fft(state->FFTbuffer, STFT_SIZE, -1.0);
|
||||
|
||||
/* Analyze the obtained data. Since the real FFT is symmetric, only
|
||||
* STFT_HALF_SIZE+1 samples are needed.
|
||||
*/
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
ALphasor component;
|
||||
ALdouble tmp;
|
||||
ALint qpd;
|
||||
|
||||
/* Compute amplitude and phase */
|
||||
component = rect2polar(state->FFTbuffer[k]);
|
||||
|
||||
/* Compute phase difference and subtract expected phase difference */
|
||||
tmp = (component.Phase - state->LastPhase[k]) - k*expected;
|
||||
|
||||
/* Map delta phase into +/- Pi interval */
|
||||
qpd = double2int(tmp / M_PI);
|
||||
tmp -= M_PI * (qpd + (qpd%2));
|
||||
|
||||
/* Get deviation from bin frequency from the +/- Pi interval */
|
||||
tmp /= expected;
|
||||
|
||||
/* Compute the k-th partials' true frequency, twice the amplitude
|
||||
* for maintain the gain (because half of bins are used) and store
|
||||
* amplitude and true frequency in analysis buffer.
|
||||
*/
|
||||
state->Analysis_buffer[k].Amplitude = 2.0 * component.Amplitude;
|
||||
state->Analysis_buffer[k].Frequency = (k + tmp) * freq_per_bin;
|
||||
|
||||
/* Store actual phase[k] for the calculations in the next frame*/
|
||||
state->LastPhase[k] = component.Phase;
|
||||
}
|
||||
|
||||
/* PROCESSING */
|
||||
/* pitch shifting */
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
state->Syntesis_buffer[k].Amplitude = 0.0;
|
||||
state->Syntesis_buffer[k].Frequency = 0.0;
|
||||
}
|
||||
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
j = (k*state->PitchShiftI) >> FRACTIONBITS;
|
||||
if(j >= STFT_HALF_SIZE+1) break;
|
||||
|
||||
state->Syntesis_buffer[j].Amplitude += state->Analysis_buffer[k].Amplitude;
|
||||
state->Syntesis_buffer[j].Frequency = state->Analysis_buffer[k].Frequency *
|
||||
state->PitchShift;
|
||||
}
|
||||
|
||||
/* SYNTHESIS */
|
||||
/* Synthesis the processing data */
|
||||
for(k = 0;k < STFT_HALF_SIZE+1;k++)
|
||||
{
|
||||
ALphasor component;
|
||||
ALdouble tmp;
|
||||
|
||||
/* Compute bin deviation from scaled freq */
|
||||
tmp = state->Syntesis_buffer[k].Frequency/freq_per_bin - k;
|
||||
|
||||
/* Calculate actual delta phase and accumulate it to get bin phase */
|
||||
state->SumPhase[k] += (k + tmp) * expected;
|
||||
|
||||
component.Amplitude = state->Syntesis_buffer[k].Amplitude;
|
||||
component.Phase = state->SumPhase[k];
|
||||
|
||||
/* Compute phasor component to cartesian complex number and storage it into FFTbuffer*/
|
||||
state->FFTbuffer[k] = polar2rect(component);
|
||||
}
|
||||
/* zero negative frequencies for recontruct a real signal */
|
||||
for(k = STFT_HALF_SIZE+1;k < STFT_SIZE;k++)
|
||||
{
|
||||
state->FFTbuffer[k].Real = 0.0;
|
||||
state->FFTbuffer[k].Imag = 0.0;
|
||||
}
|
||||
|
||||
/* Apply iFFT to buffer data */
|
||||
complex_fft(state->FFTbuffer, STFT_SIZE, 1.0);
|
||||
|
||||
/* Windowing and add to output */
|
||||
for(k = 0;k < STFT_SIZE;k++)
|
||||
state->OutputAccum[k] += HannWindow[k] * state->FFTbuffer[k].Real /
|
||||
(0.5 * STFT_HALF_SIZE * OVERSAMP);
|
||||
|
||||
/* Shift accumulator, input & output FIFO */
|
||||
for(k = 0;k < STFT_STEP;k++) state->OutFIFO[k] = (ALfloat)state->OutputAccum[k];
|
||||
for(j = 0;k < STFT_SIZE;k++,j++) state->OutputAccum[j] = state->OutputAccum[k];
|
||||
for(;j < STFT_SIZE;j++) state->OutputAccum[j] = 0.0;
|
||||
for(k = 0;k < FIFO_LATENCY;k++)
|
||||
state->InFIFO[k] = state->InFIFO[k+STFT_STEP];
|
||||
}
|
||||
state->count = count;
|
||||
|
||||
/* Now, mix the processed sound data to the output. */
|
||||
MixSamples(bufferOut, NumChannels, SamplesOut, state->CurrentGains, state->TargetGains,
|
||||
maxi(SamplesToDo, 512), 0, SamplesToDo);
|
||||
}
|
||||
|
||||
typedef struct PshifterStateFactory {
|
||||
DERIVE_FROM_TYPE(EffectStateFactory);
|
||||
} PshifterStateFactory;
|
||||
|
||||
static ALeffectState *PshifterStateFactory_create(PshifterStateFactory *UNUSED(factory))
|
||||
{
|
||||
ALpshifterState *state;
|
||||
|
||||
NEW_OBJ0(state, ALpshifterState)();
|
||||
if(!state) return NULL;
|
||||
|
||||
return STATIC_CAST(ALeffectState, state);
|
||||
}
|
||||
|
||||
DEFINE_EFFECTSTATEFACTORY_VTABLE(PshifterStateFactory);
|
||||
|
||||
EffectStateFactory *PshifterStateFactory_getFactory(void)
|
||||
{
|
||||
static PshifterStateFactory PshifterFactory = { { GET_VTABLE2(PshifterStateFactory, EffectStateFactory) } };
|
||||
|
||||
return STATIC_CAST(EffectStateFactory, &PshifterFactory);
|
||||
}
|
||||
|
||||
|
||||
void ALpshifter_setParamf(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat UNUSED(val))
|
||||
{
|
||||
alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param );
|
||||
}
|
||||
|
||||
void ALpshifter_setParamfv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALfloat *UNUSED(vals))
|
||||
{
|
||||
alSetError( context, AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", param );
|
||||
}
|
||||
|
||||
void ALpshifter_setParami(ALeffect *effect, ALCcontext *context, ALenum param, ALint val)
|
||||
{
|
||||
ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter coarse tune out of range");
|
||||
props->Pshifter.CoarseTune = val;
|
||||
break;
|
||||
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,"Pitch shifter fine tune out of range");
|
||||
props->Pshifter.FineTune = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALpshifter_setParamiv(ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals)
|
||||
{
|
||||
ALpshifter_setParami(effect, context, param, vals[0]);
|
||||
}
|
||||
|
||||
void ALpshifter_getParami(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *val)
|
||||
{
|
||||
const ALeffectProps *props = &effect->Props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
*val = (ALint)props->Pshifter.CoarseTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
*val = (ALint)props->Pshifter.FineTune;
|
||||
break;
|
||||
|
||||
default:
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", param);
|
||||
}
|
||||
}
|
||||
void ALpshifter_getParamiv(const ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals)
|
||||
{
|
||||
ALpshifter_getParami(effect, context, param, vals);
|
||||
}
|
||||
|
||||
void ALpshifter_getParamf(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(val))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param);
|
||||
}
|
||||
|
||||
void ALpshifter_getParamfv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALfloat *UNUSED(vals))
|
||||
{
|
||||
alSetError(context, AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", param);
|
||||
}
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(ALpshifter);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
#ifndef ALC_FILTER_H
|
||||
#define ALC_FILTER_H
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "math_defs.h"
|
||||
|
||||
/* Filters implementation is based on the "Cookbook formulae for audio
|
||||
* EQ biquad filter coefficients" by Robert Bristow-Johnson
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
*/
|
||||
/* Implementation note: For the shelf filters, the specified gain is for the
|
||||
* reference frequency, which is the centerpoint of the transition band. This
|
||||
* better matches EFX filter design. To set the gain for the shelf itself, use
|
||||
* the square root of the desired linear gain (or halve the dB gain).
|
||||
*/
|
||||
|
||||
typedef enum BiquadType {
|
||||
/** EFX-style low-pass filter, specifying a gain and reference frequency. */
|
||||
BiquadType_HighShelf,
|
||||
/** EFX-style high-pass filter, specifying a gain and reference frequency. */
|
||||
BiquadType_LowShelf,
|
||||
/** Peaking filter, specifying a gain and reference frequency. */
|
||||
BiquadType_Peaking,
|
||||
|
||||
/** Low-pass cut-off filter, specifying a cut-off frequency. */
|
||||
BiquadType_LowPass,
|
||||
/** High-pass cut-off filter, specifying a cut-off frequency. */
|
||||
BiquadType_HighPass,
|
||||
/** Band-pass filter, specifying a center frequency. */
|
||||
BiquadType_BandPass,
|
||||
} BiquadType;
|
||||
|
||||
typedef struct BiquadFilter {
|
||||
ALfloat z1, z2; /* Last two delayed components for direct form II. */
|
||||
ALfloat b0, b1, b2; /* Transfer function coefficients "b" (numerator) */
|
||||
ALfloat a1, a2; /* Transfer function coefficients "a" (denominator; a0 is
|
||||
* pre-applied). */
|
||||
} BiquadFilter;
|
||||
/* Currently only a C-based filter process method is implemented. */
|
||||
#define BiquadFilter_process BiquadFilter_processC
|
||||
|
||||
/**
|
||||
* Calculates the rcpQ (i.e. 1/Q) coefficient for shelving filters, using the
|
||||
* reference gain and shelf slope parameter.
|
||||
* \param gain 0 < gain
|
||||
* \param slope 0 < slope <= 1
|
||||
*/
|
||||
inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope)
|
||||
{
|
||||
return sqrtf((gain + 1.0f/gain)*(1.0f/slope - 1.0f) + 2.0f);
|
||||
}
|
||||
/**
|
||||
* Calculates the rcpQ (i.e. 1/Q) coefficient for filters, using the normalized
|
||||
* reference frequency and bandwidth.
|
||||
* \param f0norm 0 < f0norm < 0.5.
|
||||
* \param bandwidth 0 < bandwidth
|
||||
*/
|
||||
inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth)
|
||||
{
|
||||
ALfloat w0 = F_TAU * f0norm;
|
||||
return 2.0f*sinhf(logf(2.0f)/2.0f*bandwidth*w0/sinf(w0));
|
||||
}
|
||||
|
||||
inline void BiquadFilter_clear(BiquadFilter *filter)
|
||||
{
|
||||
filter->z1 = 0.0f;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the filter state for the specified filter type and its parameters.
|
||||
*
|
||||
* \param filter The filter object to prepare.
|
||||
* \param type The type of filter for the object to apply.
|
||||
* \param gain The gain for the reference frequency response. Only used by the
|
||||
* Shelf and Peaking filter types.
|
||||
* \param f0norm The normalized reference frequency (ref_freq / sample_rate).
|
||||
* This is the center point for the Shelf, Peaking, and BandPass
|
||||
* filter types, or the cutoff frequency for the LowPass and
|
||||
* HighPass filter types.
|
||||
* \param rcpQ The reciprocal of the Q coefficient for the filter's transition
|
||||
* band. Can be generated from calc_rcpQ_from_slope or
|
||||
* calc_rcpQ_from_bandwidth depending on the available data.
|
||||
*/
|
||||
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ);
|
||||
|
||||
inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src)
|
||||
{
|
||||
dst->b0 = src->b0;
|
||||
dst->b1 = src->b1;
|
||||
dst->b2 = src->b2;
|
||||
dst->a1 = src->a1;
|
||||
dst->a2 = src->a2;
|
||||
}
|
||||
|
||||
void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples);
|
||||
|
||||
inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples)
|
||||
{
|
||||
if(LIKELY(numsamples >= 2))
|
||||
{
|
||||
filter->z1 = 0.0f;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
else if(numsamples == 1)
|
||||
{
|
||||
filter->z1 = filter->z2;
|
||||
filter->z2 = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ALC_FILTER_H */
|
||||
@@ -0,0 +1,129 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "defs.h"
|
||||
|
||||
extern inline void BiquadFilter_clear(BiquadFilter *filter);
|
||||
extern inline void BiquadFilter_copyParams(BiquadFilter *restrict dst, const BiquadFilter *restrict src);
|
||||
extern inline void BiquadFilter_passthru(BiquadFilter *filter, ALsizei numsamples);
|
||||
extern inline ALfloat calc_rcpQ_from_slope(ALfloat gain, ALfloat slope);
|
||||
extern inline ALfloat calc_rcpQ_from_bandwidth(ALfloat f0norm, ALfloat bandwidth);
|
||||
|
||||
|
||||
void BiquadFilter_setParams(BiquadFilter *filter, BiquadType type, ALfloat gain, ALfloat f0norm, ALfloat rcpQ)
|
||||
{
|
||||
ALfloat alpha, sqrtgain_alpha_2;
|
||||
ALfloat w0, sin_w0, cos_w0;
|
||||
ALfloat a[3] = { 1.0f, 0.0f, 0.0f };
|
||||
ALfloat b[3] = { 1.0f, 0.0f, 0.0f };
|
||||
|
||||
// Limit gain to -100dB
|
||||
assert(gain > 0.00001f);
|
||||
|
||||
w0 = F_TAU * f0norm;
|
||||
sin_w0 = sinf(w0);
|
||||
cos_w0 = cosf(w0);
|
||||
alpha = sin_w0/2.0f * rcpQ;
|
||||
|
||||
/* Calculate filter coefficients depending on filter type */
|
||||
switch(type)
|
||||
{
|
||||
case BiquadType_HighShelf:
|
||||
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
|
||||
b[0] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
|
||||
b[1] = -2.0f*gain*((gain-1.0f) + (gain+1.0f)*cos_w0 );
|
||||
b[2] = gain*((gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
|
||||
a[0] = (gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
|
||||
a[1] = 2.0f* ((gain-1.0f) - (gain+1.0f)*cos_w0 );
|
||||
a[2] = (gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
|
||||
break;
|
||||
case BiquadType_LowShelf:
|
||||
sqrtgain_alpha_2 = 2.0f * sqrtf(gain) * alpha;
|
||||
b[0] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 + sqrtgain_alpha_2);
|
||||
b[1] = 2.0f*gain*((gain-1.0f) - (gain+1.0f)*cos_w0 );
|
||||
b[2] = gain*((gain+1.0f) - (gain-1.0f)*cos_w0 - sqrtgain_alpha_2);
|
||||
a[0] = (gain+1.0f) + (gain-1.0f)*cos_w0 + sqrtgain_alpha_2;
|
||||
a[1] = -2.0f* ((gain-1.0f) + (gain+1.0f)*cos_w0 );
|
||||
a[2] = (gain+1.0f) + (gain-1.0f)*cos_w0 - sqrtgain_alpha_2;
|
||||
break;
|
||||
case BiquadType_Peaking:
|
||||
gain = sqrtf(gain);
|
||||
b[0] = 1.0f + alpha * gain;
|
||||
b[1] = -2.0f * cos_w0;
|
||||
b[2] = 1.0f - alpha * gain;
|
||||
a[0] = 1.0f + alpha / gain;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha / gain;
|
||||
break;
|
||||
|
||||
case BiquadType_LowPass:
|
||||
b[0] = (1.0f - cos_w0) / 2.0f;
|
||||
b[1] = 1.0f - cos_w0;
|
||||
b[2] = (1.0f - cos_w0) / 2.0f;
|
||||
a[0] = 1.0f + alpha;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha;
|
||||
break;
|
||||
case BiquadType_HighPass:
|
||||
b[0] = (1.0f + cos_w0) / 2.0f;
|
||||
b[1] = -(1.0f + cos_w0);
|
||||
b[2] = (1.0f + cos_w0) / 2.0f;
|
||||
a[0] = 1.0f + alpha;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha;
|
||||
break;
|
||||
case BiquadType_BandPass:
|
||||
b[0] = alpha;
|
||||
b[1] = 0;
|
||||
b[2] = -alpha;
|
||||
a[0] = 1.0f + alpha;
|
||||
a[1] = -2.0f * cos_w0;
|
||||
a[2] = 1.0f - alpha;
|
||||
break;
|
||||
}
|
||||
|
||||
filter->a1 = a[1] / a[0];
|
||||
filter->a2 = a[2] / a[0];
|
||||
filter->b0 = b[0] / a[0];
|
||||
filter->b1 = b[1] / a[0];
|
||||
filter->b2 = b[2] / a[0];
|
||||
}
|
||||
|
||||
|
||||
void BiquadFilter_processC(BiquadFilter *filter, ALfloat *restrict dst, const ALfloat *restrict src, ALsizei numsamples)
|
||||
{
|
||||
const ALfloat a1 = filter->a1;
|
||||
const ALfloat a2 = filter->a2;
|
||||
const ALfloat b0 = filter->b0;
|
||||
const ALfloat b1 = filter->b1;
|
||||
const ALfloat b2 = filter->b2;
|
||||
ALfloat z1 = filter->z1;
|
||||
ALfloat z2 = filter->z2;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
/* Processing loop is Transposed Direct Form II. This requires less storage
|
||||
* compared to Direct Form I (only two delay components, instead of a four-
|
||||
* sample history; the last two inputs and outputs), and works better for
|
||||
* floating-point which favors summing similarly-sized values while being
|
||||
* less bothered by overflow.
|
||||
*
|
||||
* See: http://www.earlevel.com/main/2003/02/28/biquads/
|
||||
*/
|
||||
for(i = 0;i < numsamples;i++)
|
||||
{
|
||||
ALfloat input = src[i];
|
||||
ALfloat output = input*b0 + z1;
|
||||
z1 = input*b1 - output*a1 + z2;
|
||||
z2 = input*b2 - output*a2;
|
||||
dst[i] = output;
|
||||
}
|
||||
|
||||
filter->z1 = z1;
|
||||
filter->z2 = z2;
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "nfc.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Near-field control filters are the basis for handling the near-field effect.
|
||||
* The near-field effect is a bass-boost present in the directional components
|
||||
* of a recorded signal, created as a result of the wavefront curvature (itself
|
||||
* a function of sound distance). Proper reproduction dictates this be
|
||||
* compensated for using a bass-cut given the playback speaker distance, to
|
||||
* avoid excessive bass in the playback.
|
||||
*
|
||||
* For real-time rendered audio, emulating the near-field effect based on the
|
||||
* sound source's distance, and subsequently compensating for it at output
|
||||
* based on the speaker distances, can create a more realistic perception of
|
||||
* sound distance beyond a simple 1/r attenuation.
|
||||
*
|
||||
* These filters do just that. Each one applies a low-shelf filter, created as
|
||||
* the combination of a bass-boost for a given sound source distance (near-
|
||||
* field emulation) along with a bass-cut for a given control/speaker distance
|
||||
* (near-field compensation).
|
||||
*
|
||||
* Note that it is necessary to apply a cut along with the boost, since the
|
||||
* boost alone is unstable in higher-order ambisonics as it causes an infinite
|
||||
* DC gain (even first-order ambisonics requires there to be no DC offset for
|
||||
* the boost to work). Consequently, ambisonics requires a control parameter to
|
||||
* be used to avoid an unstable boost-only filter. NFC-HOA defines this control
|
||||
* as a reference delay, calculated with:
|
||||
*
|
||||
* reference_delay = control_distance / speed_of_sound
|
||||
*
|
||||
* This means w0 (for input) or w1 (for output) should be set to:
|
||||
*
|
||||
* wN = 1 / (reference_delay * sample_rate)
|
||||
*
|
||||
* when dealing with NFC-HOA content. For FOA input content, which does not
|
||||
* specify a reference_delay variable, w0 should be set to 0 to apply only
|
||||
* near-field compensation for output. It's important that w1 be a finite,
|
||||
* positive, non-0 value or else the bass-boost will become unstable again.
|
||||
* Also, w0 should not be too large compared to w1, to avoid excessively loud
|
||||
* low frequencies.
|
||||
*/
|
||||
|
||||
static const float B[4][3] = {
|
||||
{ 0.0f },
|
||||
{ 1.0f },
|
||||
{ 3.0f, 3.0f },
|
||||
{ 3.6778f, 6.4595f, 2.3222f },
|
||||
/*{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }*/
|
||||
};
|
||||
|
||||
static void NfcFilterCreate1(struct NfcFilter1 *nfc, const float w0, const float w1)
|
||||
{
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
nfc->base_gain = 1.0f;
|
||||
nfc->gain = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
b_00 = B[1][0] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->gain *= g_0;
|
||||
nfc->b1 = 2.0f * b_00 / g_0;
|
||||
|
||||
/* Calculate bass-cut coefficients. */
|
||||
r = 0.5f * w1;
|
||||
b_00 = B[1][0] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->base_gain /= g_0;
|
||||
nfc->gain /= g_0;
|
||||
nfc->a1 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
static void NfcFilterAdjust1(struct NfcFilter1 *nfc, const float w0)
|
||||
{
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
r = 0.5f * w0;
|
||||
b_00 = B[1][0] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->gain = nfc->base_gain * g_0;
|
||||
nfc->b1 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
|
||||
static void NfcFilterCreate2(struct NfcFilter2 *nfc, const float w0, const float w1)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float r;
|
||||
|
||||
nfc->base_gain = 1.0f;
|
||||
nfc->gain = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[2][0] * r;
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->gain *= g_1;
|
||||
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
|
||||
nfc->b2 = 4.0f * b_11 / g_1;
|
||||
|
||||
/* Calculate bass-cut coefficients. */
|
||||
r = 0.5f * w1;
|
||||
b_10 = B[2][0] * r;
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->base_gain /= g_1;
|
||||
nfc->gain /= g_1;
|
||||
nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
|
||||
nfc->a2 = 4.0f * b_11 / g_1;
|
||||
}
|
||||
|
||||
static void NfcFilterAdjust2(struct NfcFilter2 *nfc, const float w0)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float r;
|
||||
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[2][0] * r;
|
||||
b_11 = B[2][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->gain = nfc->base_gain * g_1;
|
||||
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
|
||||
nfc->b2 = 4.0f * b_11 / g_1;
|
||||
}
|
||||
|
||||
|
||||
static void NfcFilterCreate3(struct NfcFilter3 *nfc, const float w0, const float w1)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
nfc->base_gain = 1.0f;
|
||||
nfc->gain = 1.0f;
|
||||
|
||||
/* Calculate bass-boost coefficients. */
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[3][0] * r;
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->gain *= g_1;
|
||||
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
|
||||
nfc->b2 = 4.0f * b_11 / g_1;
|
||||
|
||||
b_00 = B[3][2] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->gain *= g_0;
|
||||
nfc->b3 = 2.0f * b_00 / g_0;
|
||||
|
||||
/* Calculate bass-cut coefficients. */
|
||||
r = 0.5f * w1;
|
||||
b_10 = B[3][0] * r;
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->base_gain /= g_1;
|
||||
nfc->gain /= g_1;
|
||||
nfc->a1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
|
||||
nfc->a2 = 4.0f * b_11 / g_1;
|
||||
|
||||
b_00 = B[3][2] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->base_gain /= g_0;
|
||||
nfc->gain /= g_0;
|
||||
nfc->a3 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
static void NfcFilterAdjust3(struct NfcFilter3 *nfc, const float w0)
|
||||
{
|
||||
float b_10, b_11, g_1;
|
||||
float b_00, g_0;
|
||||
float r;
|
||||
|
||||
r = 0.5f * w0;
|
||||
b_10 = B[3][0] * r;
|
||||
b_11 = B[3][1] * r * r;
|
||||
g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->gain = nfc->base_gain * g_1;
|
||||
nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1;
|
||||
nfc->b2 = 4.0f * b_11 / g_1;
|
||||
|
||||
b_00 = B[3][2] * r;
|
||||
g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->gain *= g_0;
|
||||
nfc->b3 = 2.0f * b_00 / g_0;
|
||||
}
|
||||
|
||||
|
||||
void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1)
|
||||
{
|
||||
memset(nfc, 0, sizeof(*nfc));
|
||||
NfcFilterCreate1(&nfc->first, w0, w1);
|
||||
NfcFilterCreate2(&nfc->second, w0, w1);
|
||||
NfcFilterCreate3(&nfc->third, w0, w1);
|
||||
}
|
||||
|
||||
void NfcFilterAdjust(NfcFilter *nfc, const float w0)
|
||||
{
|
||||
NfcFilterAdjust1(&nfc->first, w0);
|
||||
NfcFilterAdjust2(&nfc->second, w0);
|
||||
NfcFilterAdjust3(&nfc->third, w0);
|
||||
}
|
||||
|
||||
|
||||
void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float gain = nfc->first.gain;
|
||||
const float b1 = nfc->first.b1;
|
||||
const float a1 = nfc->first.a1;
|
||||
float z1 = nfc->first.z[0];
|
||||
int i;
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
float y = src[i]*gain - a1*z1;
|
||||
float out = y + b1*z1;
|
||||
z1 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->first.z[0] = z1;
|
||||
}
|
||||
|
||||
void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float gain = nfc->second.gain;
|
||||
const float b1 = nfc->second.b1;
|
||||
const float b2 = nfc->second.b2;
|
||||
const float a1 = nfc->second.a1;
|
||||
const float a2 = nfc->second.a2;
|
||||
float z1 = nfc->second.z[0];
|
||||
float z2 = nfc->second.z[1];
|
||||
int i;
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
float y = src[i]*gain - a1*z1 - a2*z2;
|
||||
float out = y + b1*z1 + b2*z2;
|
||||
z2 += z1;
|
||||
z1 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->second.z[0] = z1;
|
||||
nfc->second.z[1] = z2;
|
||||
}
|
||||
|
||||
void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count)
|
||||
{
|
||||
const float gain = nfc->third.gain;
|
||||
const float b1 = nfc->third.b1;
|
||||
const float b2 = nfc->third.b2;
|
||||
const float b3 = nfc->third.b3;
|
||||
const float a1 = nfc->third.a1;
|
||||
const float a2 = nfc->third.a2;
|
||||
const float a3 = nfc->third.a3;
|
||||
float z1 = nfc->third.z[0];
|
||||
float z2 = nfc->third.z[1];
|
||||
float z3 = nfc->third.z[2];
|
||||
int i;
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
float y = src[i]*gain - a1*z1 - a2*z2;
|
||||
float out = y + b1*z1 + b2*z2;
|
||||
z2 += z1;
|
||||
z1 += y;
|
||||
|
||||
y = out - a3*z3;
|
||||
out = y + b3*z3;
|
||||
z3 += y;
|
||||
|
||||
dst[i] = out;
|
||||
}
|
||||
nfc->third.z[0] = z1;
|
||||
nfc->third.z[1] = z2;
|
||||
nfc->third.z[2] = z3;
|
||||
}
|
||||
|
||||
#if 0 /* Original methods the above are derived from. */
|
||||
static void NfcFilterCreate(NfcFilter *nfc, const ALsizei order, const float src_dist, const float ctl_dist, const float rate)
|
||||
{
|
||||
static const float B[4][5] = {
|
||||
{ },
|
||||
{ 1.0f },
|
||||
{ 3.0f, 3.0f },
|
||||
{ 3.6778f, 6.4595f, 2.3222f },
|
||||
{ 4.2076f, 11.4877f, 5.7924f, 9.1401f }
|
||||
};
|
||||
float w0 = SPEEDOFSOUNDMETRESPERSEC / (src_dist * rate);
|
||||
float w1 = SPEEDOFSOUNDMETRESPERSEC / (ctl_dist * rate);
|
||||
ALsizei i;
|
||||
float r;
|
||||
|
||||
nfc->g = 1.0f;
|
||||
nfc->coeffs[0] = 1.0f;
|
||||
|
||||
/* NOTE: Slight adjustment from the literature to raise the center
|
||||
* frequency a bit (0.5 -> 1.0).
|
||||
*/
|
||||
r = 1.0f * w0;
|
||||
for(i = 0; i < (order-1);i += 2)
|
||||
{
|
||||
float b_10 = B[order][i ] * r;
|
||||
float b_11 = B[order][i+1] * r * r;
|
||||
float g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->b[i] = b_10;
|
||||
nfc->b[i + 1] = b_11;
|
||||
nfc->coeffs[0] *= g_1;
|
||||
nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
|
||||
}
|
||||
if(i < order)
|
||||
{
|
||||
float b_00 = B[order][i] * r;
|
||||
float g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->b[i] = b_00;
|
||||
nfc->coeffs[0] *= g_0;
|
||||
nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
|
||||
}
|
||||
|
||||
r = 1.0f * w1;
|
||||
for(i = 0;i < (order-1);i += 2)
|
||||
{
|
||||
float b_10 = B[order][i ] * r;
|
||||
float b_11 = B[order][i+1] * r * r;
|
||||
float g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->g /= g_1;
|
||||
nfc->coeffs[0] /= g_1;
|
||||
nfc->coeffs[order+i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[order+i+2] = (4.0f * b_11) / g_1;
|
||||
}
|
||||
if(i < order)
|
||||
{
|
||||
float b_00 = B[order][i] * r;
|
||||
float g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->g /= g_0;
|
||||
nfc->coeffs[0] /= g_0;
|
||||
nfc->coeffs[order+i+1] = (2.0f * b_00) / g_0;
|
||||
}
|
||||
|
||||
for(i = 0; i < MAX_AMBI_ORDER; i++)
|
||||
nfc->history[i] = 0.0f;
|
||||
}
|
||||
|
||||
static void NfcFilterAdjust(NfcFilter *nfc, const float distance)
|
||||
{
|
||||
int i;
|
||||
|
||||
nfc->coeffs[0] = nfc->g;
|
||||
|
||||
for(i = 0;i < (nfc->order-1);i += 2)
|
||||
{
|
||||
float b_10 = nfc->b[i] / distance;
|
||||
float b_11 = nfc->b[i+1] / (distance * distance);
|
||||
float g_1 = 1.0f + b_10 + b_11;
|
||||
|
||||
nfc->coeffs[0] *= g_1;
|
||||
nfc->coeffs[i+1] = ((2.0f * b_10) + (4.0f * b_11)) / g_1;
|
||||
nfc->coeffs[i+2] = (4.0f * b_11) / g_1;
|
||||
}
|
||||
if(i < nfc->order)
|
||||
{
|
||||
float b_00 = nfc->b[i] / distance;
|
||||
float g_0 = 1.0f + b_00;
|
||||
|
||||
nfc->coeffs[0] *= g_0;
|
||||
nfc->coeffs[i+1] = (2.0f * b_00) / g_0;
|
||||
}
|
||||
}
|
||||
|
||||
static float NfcFilterProcess(const float in, NfcFilter *nfc)
|
||||
{
|
||||
int i;
|
||||
float out = in * nfc->coeffs[0];
|
||||
|
||||
for(i = 0;i < (nfc->order-1);i += 2)
|
||||
{
|
||||
float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) -
|
||||
(nfc->coeffs[nfc->order+i+2] * nfc->history[i+1]) + 1.0e-30f;
|
||||
out = y + (nfc->coeffs[i+1]*nfc->history[i]) + (nfc->coeffs[i+2]*nfc->history[i+1]);
|
||||
|
||||
nfc->history[i+1] += nfc->history[i];
|
||||
nfc->history[i] += y;
|
||||
}
|
||||
if(i < nfc->order)
|
||||
{
|
||||
float y = out - (nfc->coeffs[nfc->order+i+1] * nfc->history[i]) + 1.0e-30f;
|
||||
|
||||
out = y + (nfc->coeffs[i+1] * nfc->history[i]);
|
||||
nfc->history[i] += y;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef FILTER_NFC_H
|
||||
#define FILTER_NFC_H
|
||||
|
||||
struct NfcFilter1 {
|
||||
float base_gain, gain;
|
||||
float b1, a1;
|
||||
float z[1];
|
||||
};
|
||||
struct NfcFilter2 {
|
||||
float base_gain, gain;
|
||||
float b1, b2, a1, a2;
|
||||
float z[2];
|
||||
};
|
||||
struct NfcFilter3 {
|
||||
float base_gain, gain;
|
||||
float b1, b2, b3, a1, a2, a3;
|
||||
float z[3];
|
||||
};
|
||||
|
||||
typedef struct NfcFilter {
|
||||
struct NfcFilter1 first;
|
||||
struct NfcFilter2 second;
|
||||
struct NfcFilter3 third;
|
||||
} NfcFilter;
|
||||
|
||||
|
||||
/* NOTE:
|
||||
* w0 = speed_of_sound / (source_distance * sample_rate);
|
||||
* w1 = speed_of_sound / (control_distance * sample_rate);
|
||||
*
|
||||
* Generally speaking, the control distance should be approximately the average
|
||||
* speaker distance, or based on the reference delay if outputing NFC-HOA. It
|
||||
* must not be negative, 0, or infinite. The source distance should not be too
|
||||
* small relative to the control distance.
|
||||
*/
|
||||
|
||||
void NfcFilterCreate(NfcFilter *nfc, const float w0, const float w1);
|
||||
void NfcFilterAdjust(NfcFilter *nfc, const float w0);
|
||||
|
||||
/* Near-field control filter for first-order ambisonic channels (1-3). */
|
||||
void NfcFilterProcess1(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
|
||||
|
||||
/* Near-field control filter for second-order ambisonic channels (4-8). */
|
||||
void NfcFilterProcess2(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
|
||||
|
||||
/* Near-field control filter for third-order ambisonic channels (9-15). */
|
||||
void NfcFilterProcess3(NfcFilter *nfc, float *restrict dst, const float *restrict src, const int count);
|
||||
|
||||
#endif /* FILTER_NFC_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "splitter.h"
|
||||
|
||||
#include "math_defs.h"
|
||||
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat f0norm)
|
||||
{
|
||||
ALfloat w = f0norm * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->lp_z1 = 0.0f;
|
||||
splitter->lp_z2 = 0.0f;
|
||||
splitter->hp_z1 = 0.0f;
|
||||
}
|
||||
|
||||
void bandsplit_clear(BandSplitter *splitter)
|
||||
{
|
||||
splitter->lp_z1 = 0.0f;
|
||||
splitter->lp_z2 = 0.0f;
|
||||
splitter->hp_z1 = 0.0f;
|
||||
}
|
||||
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count)
|
||||
{
|
||||
ALfloat lp_coeff, hp_coeff, lp_y, hp_y, d;
|
||||
ALfloat lp_z1, lp_z2, hp_z1;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
hp_coeff = splitter->coeff;
|
||||
lp_coeff = splitter->coeff*0.5f + 0.5f;
|
||||
lp_z1 = splitter->lp_z1;
|
||||
lp_z2 = splitter->lp_z2;
|
||||
hp_z1 = splitter->hp_z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
ALfloat in = input[i];
|
||||
|
||||
/* Low-pass sample processing. */
|
||||
d = (in - lp_z1) * lp_coeff;
|
||||
lp_y = lp_z1 + d;
|
||||
lp_z1 = lp_y + d;
|
||||
|
||||
d = (lp_y - lp_z2) * lp_coeff;
|
||||
lp_y = lp_z2 + d;
|
||||
lp_z2 = lp_y + d;
|
||||
|
||||
lpout[i] = lp_y;
|
||||
|
||||
/* All-pass sample processing. */
|
||||
hp_y = in*hp_coeff + hp_z1;
|
||||
hp_z1 = in - hp_y*hp_coeff;
|
||||
|
||||
/* High-pass generated from removing low-passed output. */
|
||||
hpout[i] = hp_y - lp_y;
|
||||
}
|
||||
splitter->lp_z1 = lp_z1;
|
||||
splitter->lp_z2 = lp_z2;
|
||||
splitter->hp_z1 = hp_z1;
|
||||
}
|
||||
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm)
|
||||
{
|
||||
ALfloat w = f0norm * F_TAU;
|
||||
ALfloat cw = cosf(w);
|
||||
if(cw > FLT_EPSILON)
|
||||
splitter->coeff = (sinf(w) - 1.0f) / cw;
|
||||
else
|
||||
splitter->coeff = cw * -0.5f;
|
||||
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_clear(SplitterAllpass *splitter)
|
||||
{
|
||||
splitter->z1 = 0.0f;
|
||||
}
|
||||
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count)
|
||||
{
|
||||
ALfloat coeff, in, out;
|
||||
ALfloat z1;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
coeff = splitter->coeff;
|
||||
z1 = splitter->z1;
|
||||
for(i = 0;i < count;i++)
|
||||
{
|
||||
in = samples[i];
|
||||
|
||||
out = in*coeff + z1;
|
||||
z1 = in - out*coeff;
|
||||
|
||||
samples[i] = out;
|
||||
}
|
||||
splitter->z1 = z1;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef FILTER_SPLITTER_H
|
||||
#define FILTER_SPLITTER_H
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
|
||||
/* Band splitter. Splits a signal into two phase-matching frequency bands. */
|
||||
typedef struct BandSplitter {
|
||||
ALfloat coeff;
|
||||
ALfloat lp_z1;
|
||||
ALfloat lp_z2;
|
||||
ALfloat hp_z1;
|
||||
} BandSplitter;
|
||||
|
||||
void bandsplit_init(BandSplitter *splitter, ALfloat f0norm);
|
||||
void bandsplit_clear(BandSplitter *splitter);
|
||||
void bandsplit_process(BandSplitter *splitter, ALfloat *restrict hpout, ALfloat *restrict lpout,
|
||||
const ALfloat *input, ALsizei count);
|
||||
|
||||
/* The all-pass portion of the band splitter. Applies the same phase shift
|
||||
* without splitting the signal.
|
||||
*/
|
||||
typedef struct SplitterAllpass {
|
||||
ALfloat coeff;
|
||||
ALfloat z1;
|
||||
} SplitterAllpass;
|
||||
|
||||
void splitterap_init(SplitterAllpass *splitter, ALfloat f0norm);
|
||||
void splitterap_clear(SplitterAllpass *splitter);
|
||||
void splitterap_process(SplitterAllpass *splitter, ALfloat *restrict samples, ALsizei count);
|
||||
|
||||
|
||||
typedef struct FrontStablizer {
|
||||
SplitterAllpass APFilter[MAX_OUTPUT_CHANNELS];
|
||||
BandSplitter LFilter, RFilter;
|
||||
alignas(16) ALfloat LSplit[2][BUFFERSIZE];
|
||||
alignas(16) ALfloat RSplit[2][BUFFERSIZE];
|
||||
} FrontStablizer;
|
||||
|
||||
#endif /* FILTER_SPLITTER_H */
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef FPU_MODES_H
|
||||
#define FPU_MODES_H
|
||||
|
||||
#ifdef HAVE_FENV_H
|
||||
#include <fenv.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct FPUCtl {
|
||||
#if defined(__GNUC__) && defined(HAVE_SSE)
|
||||
unsigned int sse_state;
|
||||
#elif defined(HAVE___CONTROL87_2)
|
||||
unsigned int state;
|
||||
unsigned int sse_state;
|
||||
#elif defined(HAVE__CONTROLFP)
|
||||
unsigned int state;
|
||||
#endif
|
||||
} FPUCtl;
|
||||
void SetMixerFPUMode(FPUCtl *ctl);
|
||||
void RestoreFPUMode(const FPUCtl *ctl);
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Use an alternate macro set with GCC to avoid accidental continue or break
|
||||
* statements within the mixer mode.
|
||||
*/
|
||||
#define START_MIXER_MODE() __extension__({ FPUCtl _oldMode; SetMixerFPUMode(&_oldMode)
|
||||
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); })
|
||||
#else
|
||||
#define START_MIXER_MODE() do { FPUCtl _oldMode; SetMixerFPUMode(&_oldMode)
|
||||
#define END_MIXER_MODE() RestoreFPUMode(&_oldMode); } while(0)
|
||||
#endif
|
||||
#define LEAVE_MIXER_MODE() RestoreFPUMode(&_oldMode)
|
||||
|
||||
#endif /* FPU_MODES_H */
|
||||
+1012
-289
File diff suppressed because it is too large
Load Diff
+1385
-377
File diff suppressed because it is too large
Load Diff
+84
@@ -0,0 +1,84 @@
|
||||
#ifndef ALC_HRTF_H
|
||||
#define ALC_HRTF_H
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alstring.h"
|
||||
#include "atomic.h"
|
||||
|
||||
|
||||
#define HRTF_HISTORY_BITS (6)
|
||||
#define HRTF_HISTORY_LENGTH (1<<HRTF_HISTORY_BITS)
|
||||
#define HRTF_HISTORY_MASK (HRTF_HISTORY_LENGTH-1)
|
||||
|
||||
#define HRIR_BITS (7)
|
||||
#define HRIR_LENGTH (1<<HRIR_BITS)
|
||||
#define HRIR_MASK (HRIR_LENGTH-1)
|
||||
|
||||
|
||||
struct HrtfEntry;
|
||||
|
||||
struct Hrtf {
|
||||
RefCount ref;
|
||||
|
||||
ALuint sampleRate;
|
||||
ALsizei irSize;
|
||||
|
||||
ALfloat distance;
|
||||
ALubyte evCount;
|
||||
|
||||
const ALubyte *azCount;
|
||||
const ALushort *evOffset;
|
||||
const ALfloat (*coeffs)[2];
|
||||
const ALubyte (*delays)[2];
|
||||
};
|
||||
|
||||
|
||||
typedef struct HrtfState {
|
||||
alignas(16) ALfloat History[HRTF_HISTORY_LENGTH];
|
||||
alignas(16) ALfloat Values[HRIR_LENGTH][2];
|
||||
} HrtfState;
|
||||
|
||||
typedef struct HrtfParams {
|
||||
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
|
||||
ALsizei Delay[2];
|
||||
ALfloat Gain;
|
||||
} HrtfParams;
|
||||
|
||||
typedef struct DirectHrtfState {
|
||||
/* HRTF filter state for dry buffer content */
|
||||
ALsizei Offset;
|
||||
ALsizei IrSize;
|
||||
struct {
|
||||
alignas(16) ALfloat Values[HRIR_LENGTH][2];
|
||||
alignas(16) ALfloat Coeffs[HRIR_LENGTH][2];
|
||||
} Chan[];
|
||||
} DirectHrtfState;
|
||||
|
||||
struct AngularPoint {
|
||||
ALfloat Elev;
|
||||
ALfloat Azim;
|
||||
};
|
||||
|
||||
|
||||
void FreeHrtfs(void);
|
||||
|
||||
vector_EnumeratedHrtf EnumerateHrtf(const_al_string devname);
|
||||
void FreeHrtfList(vector_EnumeratedHrtf *list);
|
||||
struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry);
|
||||
void Hrtf_IncRef(struct Hrtf *hrtf);
|
||||
void Hrtf_DecRef(struct Hrtf *hrtf);
|
||||
|
||||
void GetHrtfCoeffs(const struct Hrtf *Hrtf, ALfloat elevation, ALfloat azimuth, ALfloat spread, ALfloat (*coeffs)[2], ALsizei *delays);
|
||||
|
||||
/**
|
||||
* Produces HRTF filter coefficients for decoding B-Format, given a set of
|
||||
* virtual speaker positions, a matching decoding matrix, and per-order high-
|
||||
* frequency gains for the decoder. The calculated impulse responses are
|
||||
* ordered and scaled according to the matrix input.
|
||||
*/
|
||||
void BuildBFormatHrtf(const struct Hrtf *Hrtf, DirectHrtfState *state, ALsizei NumChannels, const struct AngularPoint *AmbiPoints, const ALfloat (*restrict AmbiMatrix)[MAX_AMBI_COEFFS], ALsizei AmbiCount, const ALfloat *restrict AmbiOrderHFGain);
|
||||
|
||||
#endif /* ALC_HRTF_H */
|
||||
@@ -1,839 +0,0 @@
|
||||
/* This data is Copyright 1994 by the MIT Media Laboratory. It is provided free
|
||||
* with no restrictions on use, provided the authors are cited when the data is
|
||||
* used in any research or commercial application. */
|
||||
/* Bill Gardner <billg@media.mit.edu> and Keith Martin <kdm@media.mit.edu> */
|
||||
|
||||
/* HRIR Coefficients */
|
||||
{
|
||||
{ +5817, +7833, +3253, +2795, +1080, +403, +948, -690, -2207, -1110, +658, -33, -481, -949, -839, -287, -146, -103, +7, -47, -269, -105, -316, -221, -304, -497, -373, -231, -114, -14, +66, -62, },
|
||||
{ +6586, +8107, +2781, +3167, +1095, +99, +922, -641, -2255, -1457, +602, +170, -469, -781, -818, -773, -258, +8, -111, -125, -368, -216, -345, -265, -298, -447, -408, -158, -22, +0, +63, -96, },
|
||||
{ +5986, +7570, +2713, +2756, +1080, +403, +921, -758, -2112, -1067, +565, -93, -431, -738, -604, -361, -238, +10, +63, -88, -302, -101, -306, -243, -358, -488, -341, -224, -98, -1, +79, -55, },
|
||||
{ +5571, +7005, +2499, +2549, +974, +438, +959, -688, -1966, -793, +755, -35, -305, -631, -545, -165, -118, -72, +26, -36, -228, -36, -253, -158, -261, -436, -311, -197, -85, +11, +82, -47, },
|
||||
{ +5472, +6865, +2460, +2524, +978, +492, +1057, -561, -1857, -706, +813, -3, -353, -739, -587, -120, -98, -94, +21, -32, -228, -31, -232, -119, -204, -399, -293, -197, -124, -45, +34, -81, },
|
||||
{ +5627, +7081, +2487, +2449, +858, +396, +981, -676, -2051, -899, +820, +82, -332, -774, -643, -127, -26, -14, +72, +27, -183, -16, -234, -142, -240, -422, -289, -151, -36, +63, +108, -65, },
|
||||
{ +6172, +7677, +2541, +2564, +874, +397, +966, -927, -2421, -1117, +905, +38, -545, -990, -785, -200, -66, -60, +50, +17, -244, -35, -244, -146, -246, -433, -290, -139, -42, +40, +108, -37, },
|
||||
{ +6521, +8320, +2827, +2690, +909, +230, +917, -1084, -2525, -1115, +652, -122, -480, -974, -823, -289, -150, +21, +73, -58, -296, -90, -299, -198, -317, -480, -327, -206, -64, +25, +103, -47, },
|
||||
{ +6956, +8505, +2738, +3040, +1131, +209, +719, -1389, -2419, -516, +530, -857, -697, -801, -443, -77, -328, -117, +128, -70, -281, -49, -356, -233, -419, -579, -344, -271, -169, -16, +76, -98, },
|
||||
{ +7928, +9215, +2040, +2470, +595, +264, +1348, -1308, -2698, -258, +1302, -836, -1179, -1210, -483, +125, -403, -370, +81, -61, -416, -45, -378, -208, -426, -708, -383, -223, -150, -20, +82, -119, },
|
||||
{ +8205, +9593, +2241, +2306, +197, +241, +1480, -1080, -3081, -704, +1757, -153, -1144, -1906, -912, +311, -4, -441, -129, -99, -476, -104, -467, -207, -379, -660, -412, -194, -106, -31, +48, -172, },
|
||||
{ +7822, +9388, +3008, +2690, +349, +155, +1066, -976, -3351, -1396, +1366, +202, -165, -1794, -1686, +63, +209, -110, -81, -164, -375, -114, -487, -174, -394, -771, -456, -312, -109, +27, +100, -71, },
|
||||
{ +7342, +9001, +2833, +2968, +859, +118, +830, -1009, -2609, -1583, +1030, +177, -566, -888, -1564, -882, +55, +78, +62, +48, -275, -224, -531, -354, -242, -534, -487, -241, -107, +31, +92, -76, },
|
||||
{ +7420, +8277, +2251, +3723, +1049, -308, +945, -518, -2311, -1945, +592, +496, -502, -565, -848, -1420, -265, +189, -293, -192, -468, -339, -342, -319, -275, -379, -474, -58, +77, -10, +55, -134, },
|
||||
{ +6584, +7746, +2495, +3374, +1000, -52, +897, -756, -2030, -1406, +280, +34, -398, -477, -456, -856, -571, +107, +104, -158, -435, -199, -286, -253, -345, -425, -316, -178, -107, -66, +74, -62, },
|
||||
{ +6049, +7215, +2251, +2809, +1129, +395, +858, -790, -1953, -1091, +399, -101, -343, -474, -371, -537, -334, +183, +103, -155, -328, -94, -287, -281, -418, -457, -307, -226, -82, +10, +91, -45, },
|
||||
{ +5611, +6654, +2026, +2614, +1122, +421, +846, -730, -1795, -787, +486, -181, -199, -331, -331, -290, -184, +57, +64, -94, -264, -59, -241, -160, -276, -381, -321, -242, -63, +33, +98, -44, },
|
||||
{ +5099, +6036, +1921, +2401, +945, +508, +937, -640, -1638, -506, +755, -23, -74, -266, -268, -108, -114, -39, +41, -26, -172, +33, -185, -102, -220, -366, -254, -174, -58, +33, +94, -28, },
|
||||
{ +4863, +5622, +1644, +2154, +818, +547, +1098, -307, -1280, -210, +982, +96, -111, -376, -319, -58, -43, -22, +51, -10, -188, -3, -174, -47, -165, -316, -201, -114, -14, +20, +16, -134, },
|
||||
{ +4874, +5742, +1871, +2357, +961, +630, +1147, -376, -1424, -337, +863, +34, -193, -499, -332, +8, -81, -97, +36, -13, -175, +46, -137, -17, -102, -297, -226, -178, -147, -83, +1, -91, },
|
||||
{ +4960, +5827, +1797, +2194, +842, +622, +1162, -376, -1492, -421, +910, +56, -246, -568, -430, +32, +120, +83, +106, +18, -182, -19, -208, -85, -152, -240, -94, -64, -84, -82, -30, -123, },
|
||||
{ +5228, +6194, +1872, +2175, +707, +445, +1001, -617, -1837, -723, +933, +227, -167, -586, -451, +10, +86, +68, +127, +106, -88, +66, -146, -70, -175, -339, -208, -74, +41, +139, +139, -76, },
|
||||
{ +5697, +6722, +1953, +2283, +727, +469, +969, -882, -2258, -1037, +972, +175, -413, -806, -593, -28, +130, +87, +177, +163, -115, +43, -180, -84, -142, -297, -156, -24, +4, +49, +85, -29, },
|
||||
{ +6473, +7408, +1826, +2417, +711, +442, +966, -1188, -2613, -1118, +1193, +107, -644, -1020, -716, -117, +16, -26, +92, +89, -223, +39, -164, -76, -190, -362, -208, -46, +24, +86, +145, -12, },
|
||||
{ +7101, +8208, +1841, +2445, +711, +357, +985, -1505, -2879, -1121, +1181, +70, -658, -1173, -814, -168, -29, +29, +47, +36, -265, -40, -293, -166, -283, -429, -229, -63, +43, +81, +160, -20, },
|
||||
{ +7271, +8783, +2298, +2617, +755, +48, +900, -1532, -2806, -1083, +615, -189, -449, -1001, -807, -311, -150, +184, +122, -88, -322, -71, -273, -179, -335, -451, -278, -188, -9, +60, +138, -32, },
|
||||
{ +7519, +8972, +2519, +3099, +1077, -70, +527, -1818, -2665, -568, +208, -889, -424, -657, -398, -291, -483, +67, +222, -56, -219, -19, -307, -204, -436, -529, -306, -304, -126, +27, +128, -48, },
|
||||
{ +8267, +9086, +2039, +3441, +1167, -66, +449, -2175, -2478, +261, +170, -1844, -741, -537, +19, +68, -620, -86, +289, -131, -285, +19, -414, -244, -560, -647, -289, -330, -230, -5, +89, -141, },
|
||||
{ +9334, +10020, +1039, +2681, +800, -36, +1253, -2153, -2906, +675, +921, -2082, -1336, -748, +118, +362, -708, -352, +305, -89, -430, +16, -461, -267, -609, -817, -292, -288, -287, -70, +134, -107, },
|
||||
{ +10490, +10443, +234, +2319, +106, +215, +1880, -2162, -3146, +928, +1870, -2011, -1861, -1339, +44, +548, -846, -647, +252, -93, -597, +65, -464, -180, -581, -943, -344, -209, -200, -20, +104, -191, },
|
||||
{ +11090, +10859, +176, +1929, -492, +415, +2257, -1899, -3660, +617, +2693, -1436, -2083, -2245, -339, +944, -576, -854, +49, -173, -603, +64, -607, -194, -525, -910, -310, -113, -162, +10, +55, -268, },
|
||||
{ +11123, +11231, +589, +1885, -762, +272, +2193, -1677, -4087, +10, +3036, -569, -1972, -2950, -747, +1099, +20, -922, -216, -122, -721, -53, -651, -153, -474, -845, -423, -135, -107, -57, +30, -304, },
|
||||
{ +10803, +11222, +1474, +2265, -883, +91, +1845, -1667, -4470, -728, +2793, +190, -972, -3366, -1480, +1091, +471, -617, -486, -175, -639, -113, -591, -51, -507, -934, -502, -309, -106, -118, +53, -177, },
|
||||
{ +10246, +10868, +2402, +2554, -531, +31, +1268, -1323, -4728, -1500, +2335, +359, +174, -2966, -2559, +743, +585, -219, -198, -287, -474, -103, -696, -72, -517, -1094, -493, -391, -81, +78, +133, -87, },
|
||||
{ +9638, +10459, +2554, +3136, -109, -170, +1213, -1306, -4339, -2094, +2099, +264, +202, -1798, -3312, -170, +781, +126, +63, -260, -424, +20, -759, -504, -338, -983, -510, -236, -85, +153, +125, -96, },
|
||||
{ +9150, +10114, +2114, +3243, +563, -199, +731, -1335, -3018, -2131, +1610, +397, -733, -785, -2524, -1463, +491, +266, +80, +156, -304, -383, -768, -471, -118, -592, -631, -225, -95, +87, +113, -96, },
|
||||
{ +8454, +9243, +2047, +3497, +881, -249, +701, -1042, -2617, -2062, +1294, +712, -942, -676, -1397, -1797, -57, +250, -231, -14, -250, -237, -521, -454, -179, -387, -649, -192, -30, +13, +161, -121, },
|
||||
{ +8323, +8323, +1685, +4494, +893, -823, +1049, -315, -2397, -2598, +672, +957, -622, -284, -950, -2240, -109, +425, -567, -229, -570, -475, -300, -391, -228, -295, -579, +83, +176, -48, +44, -175, },
|
||||
{ +7274, +7900, +2262, +4107, +785, -714, +918, -662, -1986, -1894, +51, +378, -372, -108, -420, -1664, -750, +303, +73, -176, -577, -344, -248, -256, -255, -331, -406, -129, -13, -101, +50, -88, },
|
||||
{ +6560, +7256, +2146, +3528, +960, -9, +858, -751, -1783, -1572, +54, +101, -313, -181, -229, -1133, -648, +400, +128, -265, -444, -160, -232, -331, -449, -399, -265, -206, -153, -62, +116, -28, },
|
||||
{ +5980, +6790, +1905, +2935, +1217, +364, +757, -764, -1730, -1213, +165, -24, -228, -158, -154, -831, -410, +428, +110, -247, -340, -86, -256, -338, -480, -399, -278, -238, -61, +18, +103, -30, },
|
||||
{ +5480, +6206, +1692, +2736, +1300, +391, +709, -692, -1565, -861, +226, -147, -68, -24, -127, -550, -217, +281, +49, -181, -257, -43, -247, -249, -345, -298, -274, -270, -50, +56, +119, -43, },
|
||||
{ +4953, +5620, +1632, +2478, +1119, +519, +800, -639, -1430, -598, +380, -73, +141, +70, -109, -295, -151, +126, +65, -105, -195, +54, -97, -104, -271, -314, -265, -208, -14, +48, +99, +5, },
|
||||
{ +4358, +4972, +1588, +2310, +986, +595, +869, -526, -1223, -277, +652, +34, +216, +141, -22, -120, -122, +1, +49, -14, -101, +99, -108, -54, -180, -284, -207, -160, -31, +53, +104, -2, },
|
||||
{ +4049, +4526, +1367, +2016, +774, +619, +1057, -193, -943, +8, +975, +238, +224, +73, -43, -96, -105, -16, +104, +54, -140, +64, -50, +12, -174, -276, -142, -78, +11, +66, +70, -116, },
|
||||
{ +3893, +4241, +1180, +1972, +964, +806, +1165, +33, -630, +212, +995, +139, -10, -165, -98, +59, +41, +7, +5, -42, -115, +70, -77, +64, -30, -184, -140, -90, -15, -39, -72, -166, },
|
||||
{ +3974, +4515, +1563, +2245, +1024, +802, +1202, -117, -913, -34, +802, +109, -1, -233, -83, +92, -89, -104, +51, +9, -107, +121, -30, +82, +5, -192, -173, -173, -183, -127, -31, -91, },
|
||||
{ +4054, +4585, +1487, +2101, +966, +877, +1249, -118, -989, -177, +776, +97, -55, -258, -131, +135, +150, +93, +56, -19, -114, +43, -129, +22, -1, -45, +2, -139, -203, -156, -63, -102, },
|
||||
{ +4259, +4784, +1375, +1894, +699, +649, +1161, -136, -1098, -231, +956, +281, +11, -419, -371, +109, +241, +228, +268, +110, -100, +71, -147, -93, -92, -157, -42, +95, +87, -12, -90, -205, },
|
||||
{ +4579, +5216, +1470, +1933, +626, +538, +991, -498, -1568, -608, +1002, +425, +4, -387, -268, +119, +196, +143, +174, +193, +16, +138, -48, -8, -110, -248, -134, +1, +116, +214, +157, -94, },
|
||||
{ +5066, +5749, +1521, +2044, +640, +538, +926, -811, -2022, -949, +1051, +371, -252, -591, -377, +160, +302, +178, +234, +199, -60, +136, -88, +2, -40, -201, -104, +47, +67, +67, +148, +54, },
|
||||
{ +5824, +6357, +1417, +2188, +604, +584, +938, -1093, -2439, -1140, +1270, +283, -550, -828, -547, +25, +209, +96, +231, +241, -75, +150, -72, -37, -72, -173, -85, +78, +65, +65, +106, -26, },
|
||||
{ +6705, +7034, +1139, +2353, +584, +531, +931, -1467, -2777, -1121, +1528, +169, -787, -1030, -630, -42, +104, -4, +133, +171, -212, +119, -74, -14, -133, -284, -130, +49, +82, +127, +179, +14, },
|
||||
{ +7494, +7838, +930, +2406, +628, +447, +939, -1854, -3086, -1058, +1655, +110, -889, -1206, -690, -58, +26, -36, +86, +157, -264, +21, -209, -75, -225, -353, -152, +18, +88, +139, +234, +25, },
|
||||
{ +7942, +8659, +1161, +2413, +549, +251, +1028, -2079, -3182, -1091, +1258, +58, -663, -1281, -862, -154, +30, +213, +47, +25, -289, -45, -300, -173, -306, -391, -167, -32, +123, +117, +202, -22, },
|
||||
{ +8068, +9218, +1660, +2593, +617, -151, +900, -2038, -3036, -1021, +535, -219, -391, -1036, -790, -354, -141, +391, +141, -138, -345, -47, -238, -167, -360, -407, -230, -180, +55, +90, +173, -18, },
|
||||
{ +8265, +9463, +2020, +3087, +909, -384, +486, -2292, -2858, -489, -42, -958, -218, -589, -423, -435, -531, +296, +255, -79, -195, -39, -305, -178, -439, -471, -257, -338, -66, +74, +175, -25, },
|
||||
{ +8828, +9479, +1858, +3692, +1200, -444, +108, -2595, -2598, +225, -443, -1837, -211, -338, +130, -297, -878, +233, +403, -155, -160, +68, -348, -198, -618, -577, -252, -401, -165, +50, +123, -106, },
|
||||
{ +9769, +9532, +1154, +4053, +1153, -433, +152, -3044, -2341, +1209, -501, -2958, -549, -180, +526, +122, -1023, +24, +484, -243, -273, +98, -497, -249, -730, -696, -208, -420, -294, +24, +104, -194, },
|
||||
{ +10963, +10314, -18, +3480, +928, -378, +839, -3146, -2679, +1815, +181, -3477, -1184, -280, +767, +456, -1173, -196, +557, -284, -445, +171, -561, -316, -811, -844, -191, -379, -352, -44, +133, -183, },
|
||||
{ +12437, +11094, -1445, +2785, +310, -110, +1926, -3295, -3171, +2280, +1217, -3589, -1908, -710, +881, +724, -1458, -605, +653, -147, -678, +163, -557, -237, -805, -1040, -224, -349, -355, -12, +190, -216, },
|
||||
{ +13565, +11394, -2213, +2503, -423, +267, +2542, -3317, -3490, +2485, +2267, -3594, -2432, -1318, +735, +938, -1508, -888, +536, -170, -812, +241, -591, -131, -780, -1195, -241, -201, -267, -8, +131, -283, },
|
||||
{ +14365, +11785, -2502, +2065, -1128, +690, +3071, -3145, -4082, +2294, +3294, -3126, -2782, -2332, +449, +1450, -1337, -1152, +323, -232, -796, +274, -707, -133, -760, -1160, -175, -77, -224, +1, +81, -344, },
|
||||
{ +14713, +12188, -2245, +1714, -1545, +697, +3166, -2769, -4728, +1796, +4066, -2366, -3022, -3257, +104, +1860, -844, -1416, +119, -254, -847, +177, -909, -97, -642, -1112, -239, -41, -190, +38, +3, -445, },
|
||||
{ +14645, +12610, -1772, +1687, -1818, +544, +3067, -2552, -5197, +1109, +4450, -1369, -2921, -4051, -284, +2048, -140, -1537, -208, -123, -1018, +64, -886, -45, -602, -1050, -397, -58, -122, -90, +12, -460, },
|
||||
{ +14311, +12744, -823, +2013, -2087, +348, +2751, -2573, -5594, +298, +4380, -359, -2003, -4793, -1003, +2181, +482, -1298, -654, -93, -995, -9, -758, +71, -678, -1141, -461, -235, -94, -220, +50, -343, },
|
||||
{ +13776, +12639, +346, +2236, -2014, +116, +2251, -2236, -6147, -504, +3963, +239, -615, -5033, -2049, +2178, +726, -876, -698, -265, -701, -87, -865, +185, -648, -1310, -522, -464, -75, -87, +92, -171, },
|
||||
{ +13149, +12169, +1402, +2459, -1565, +84, +1536, -1759, -6362, -1341, +3564, +365, +555, -4522, -3382, +1810, +905, -453, -324, -409, -562, -71, -953, +103, -691, -1468, -465, -480, -26, +135, +160, -111, },
|
||||
{ +12380, +11681, +1885, +3275, -1333, -131, +1419, -1701, -5961, -2232, +3308, +405, +924, -3382, -4610, +1021, +1316, -94, -127, -370, -464, +43, -1042, -366, -474, -1380, -504, -384, +12, +312, +117, -135, },
|
||||
{ +11903, +11393, +1499, +3646, -589, -712, +1361, -1471, -5102, -2585, +2953, +277, +167, -1768, -4726, -478, +1438, +247, +197, -198, -524, -85, -1094, -602, -205, -1156, -495, -151, -147, +245, +126, -155, },
|
||||
{ +11277, +11106, +1068, +3696, +162, -535, +662, -1668, -3438, -2767, +2450, +580, -999, -611, -3766, -1971, +1197, +402, +51, +288, -364, -586, -1018, -564, +72, -690, -805, -170, -83, +153, +125, -123, },
|
||||
{ +10588, +10248, +1041, +3990, +549, -596, +503, -1508, -2940, -2531, +2104, +852, -1356, -383, -2481, -2696, +537, +474, -251, +251, -199, -459, -926, -574, +197, -420, -1013, -187, +29, +54, +181, -131, },
|
||||
{ +9521, +9264, +1317, +4316, +722, -767, +764, -816, -2661, -2833, +1508, +1326, -1203, -394, -1458, -2679, +165, +492, -606, -94, -331, -324, -381, -536, -231, -294, -717, -35, +53, -16, +190, -183, },
|
||||
{ +9295, +8224, +1109, +5503, +566, -1438, +1272, -36, -2542, -3436, +897, +1558, -877, +95, -1150, -3243, +276, +686, -962, -210, -683, -628, -208, -496, -150, -199, -735, +279, +265, -118, +39, -219, },
|
||||
{ +7955, +7915, +2062, +4941, +392, -1429, +1053, -497, -1939, -2532, -124, +882, -416, +352, -483, -2672, -726, +593, -30, -164, -732, -489, -159, -285, -145, -245, -559, -60, +99, -150, +30, -106, },
|
||||
{ +7031, +7277, +2031, +4336, +697, -646, +955, -623, -1641, -2182, -233, +535, -317, +229, -165, -1960, -826, +699, +101, -378, -580, -260, -151, -369, -398, -280, -286, -150, -143, -139, +125, -38, },
|
||||
{ +6295, +6730, +1892, +3559, +1033, +63, +744, -685, -1521, -1804, -171, +306, -225, +149, +4, -1512, -648, +773, +80, -370, -396, -159, -189, -414, -539, -344, -273, -228, -102, -44, +122, +2, },
|
||||
{ +5567, +6186, +1652, +2969, +1443, +344, +573, -641, -1422, -1343, -105, +121, -57, +247, +28, -1174, -348, +731, +56, -360, -317, -47, -217, -426, -532, -288, -251, -274, -18, +48, +118, -19, },
|
||||
{ +4996, +5539, +1497, +2838, +1501, +319, +538, -541, -1215, -962, -91, -26, +159, +383, +48, -835, -191, +492, -4, -250, -216, -31, -205, -289, -352, -182, -273, -311, -10, +79, +133, -33, },
|
||||
{ +4369, +4911, +1493, +2561, +1337, +470, +618, -484, -1046, -662, +27, +2, +405, +487, +57, -528, -160, +283, +46, -156, -163, +53, -43, -114, -258, -209, -289, -247, +29, +65, +110, +22, },
|
||||
{ +3634, +4210, +1572, +2321, +1183, +650, +679, -395, -859, -352, +291, +111, +541, +545, +126, -276, -117, +132, +29, -68, -30, +169, -23, -64, -184, -195, -205, -180, +9, +60, +105, +51, },
|
||||
{ +3016, +3577, +1500, +2051, +987, +707, +858, -234, -596, +114, +631, +273, +558, +609, +229, -166, -145, -29, +96, +94, -41, +113, +28, +51, -118, -204, -138, -123, -4, +88, +117, +1, },
|
||||
{ +2776, +3142, +1225, +1761, +847, +791, +1067, +272, -238, +302, +902, +413, +488, +421, +138, -130, -62, +38, +115, +77, -79, +99, +64, +85, -110, -151, -69, -33, +59, +61, +9, -164, },
|
||||
{ +2597, +2855, +1075, +1771, +1129, +1005, +1152, +473, +68, +492, +881, +270, +197, +161, +118, +79, +88, +32, -20, -32, -27, +115, +32, +153, +68, -66, -90, -62, +9, -70, -139, -184, },
|
||||
{ +2669, +3145, +1471, +2033, +1221, +1035, +1183, +275, -243, +270, +725, +249, +159, +52, +151, +138, -51, -81, +23, -1, -18, +190, +86, +173, +118, -82, -134, -156, -186, -160, -81, -98, },
|
||||
{ +2819, +3343, +1567, +2048, +1169, +1075, +1248, +244, -402, -85, +455, +306, +297, +79, +134, +124, +55, +91, +34, -77, +10, +181, -46, +108, +219, +134, -56, -289, -273, -145, -92, -88, },
|
||||
{ +2900, +3386, +1384, +1773, +1008, +1084, +1261, +225, -495, +12, +783, +242, +37, -53, -9, +253, +383, +202, +102, +70, -62, +11, -40, +64, +31, +110, +190, -6, -190, -236, -126, -117, },
|
||||
{ +3231, +3701, +1275, +1612, +705, +788, +1133, +122, -729, -179, +937, +566, +221, -206, -199, +166, +308, +308, +335, +202, +36, +131, -62, -37, -38, -83, +43, +191, +169, +38, -106, -235, },
|
||||
{ +3635, +4205, +1339, +1662, +626, +662, +937, -299, -1254, -578, +1037, +699, +166, -176, -99, +200, +312, +211, +211, +294, +129, +195, +63, +39, -40, -148, -70, +74, +192, +288, +163, -117, },
|
||||
{ +4194, +4783, +1346, +1788, +630, +642, +840, -677, -1766, -952, +1124, +635, -123, -376, -187, +304, +462, +242, +276, +259, +18, +219, +17, +71, +61, -99, -59, +121, +125, +97, +206, +113, },
|
||||
{ +4979, +5362, +1202, +1940, +602, +719, +840, -1009, -2235, -1155, +1361, +510, -477, -624, -359, +169, +423, +212, +324, +400, +17, +168, -5, +20, +47, -58, +31, +174, +74, +79, +86, +27, },
|
||||
{ +6045, +6030, +859, +2185, +506, +725, +848, -1395, -2650, -1218, +1701, +338, -794, -842, -492, +56, +296, +67, +259, +328, -97, +236, +42, +27, -10, -99, -27, +180, +116, +105, +139, -10, },
|
||||
{ +7127, +6787, +391, +2413, +495, +620, +850, -1887, -2997, -1109, +1985, +195, -1031, -1079, -541, +28, +148, +5, +155, +227, -224, +200, +4, -1, -101, -203, -81, +123, +136, +165, +254, +85, },
|
||||
{ +8041, +7641, +54, +2482, +542, +525, +873, -2340, -3303, -988, +2101, +103, -1125, -1253, -590, +7, +85, -30, +110, +254, -288, +69, -159, -33, -202, -285, -79, +104, +146, +180, +291, +49, },
|
||||
{ +8670, +8596, +151, +2444, +429, +355, +1004, -2691, -3423, -1007, +1694, +127, -881, -1445, -767, -93, +121, +297, -11, +97, -290, +1, -294, -164, -296, -328, -78, +89, +211, +122, +240, -5, },
|
||||
{ +8845, +9339, +619, +2533, +443, -115, +1042, -2670, -3312, -1004, +882, -48, -483, -1247, -888, -243, +10, +511, +67, -76, -337, -56, -239, -164, -351, -335, -149, -114, +174, +149, +241, -25, },
|
||||
{ +8992, +9809, +1188, +2897, +625, -583, +690, -2700, -3089, -635, -11, -646, -104, -790, -618, -507, -344, +619, +168, -148, -236, -69, -262, -132, -414, -375, -196, -304, +74, +121, +206, -26, },
|
||||
{ +9299, +9977, +1439, +3466, +927, -820, +224, -3016, -2800, -12, -737, -1360, +110, -354, -169, -639, -802, +553, +363, -141, -128, +0, -287, -205, -550, -443, -225, -438, -58, +99, +221, -4, },
|
||||
{ +10058, +9879, +1163, +4263, +1177, -922, -219, -3311, -2434, +862, -1303, -2457, +248, -70, +493, -501, -1236, +540, +538, -275, -74, +119, -379, -193, -775, -553, -189, -513, -161, +91, +140, -128, },
|
||||
{ +11144, +9761, +369, +4862, +1071, -936, -203, -3855, -2065, +1997, -1565, -3828, +38, +198, +979, -90, -1513, +341, +661, -404, -156, +165, -600, -237, -878, -660, -142, -564, -301, +70, +104, -215, },
|
||||
{ +12546, +10246, -966, +4691, +927, -833, +309, -4199, -2112, +2959, -1144, -4789, -535, +243, +1329, +364, -1689, +85, +795, -505, -386, +288, -677, -309, -1025, -827, -61, -516, -419, +27, +137, -278, },
|
||||
{ +14045, +11382, -2788, +3760, +580, -650, +1686, -4408, -2934, +3566, -51, -5174, -1321, +73, +1428, +705, -1911, -307, +891, -369, -599, +281, -731, -286, -1048, -1082, -16, -454, -510, -50, +248, -211, },
|
||||
{ +15970, +11714, -4486, +3446, -223, -120, +2672, -4778, -3223, +4216, +1130, -5477, -2203, -468, +1756, +960, -2402, -712, +1105, -303, -935, +391, -711, -185, -1048, -1265, -82, -424, -432, +54, +241, -341, },
|
||||
{ +17220, +11916, -5327, +3210, -1072, +444, +3327, -4843, -3644, +4439, +2382, -5600, -2783, -1154, +1580, +1254, -2411, -1038, +941, -324, -1057, +499, -778, -47, -1032, -1459, -61, -214, -352, +22, +162, -398, },
|
||||
{ +18240, +12274, -5799, +2731, -1899, +1079, +3952, -4749, -4376, +4334, +3662, -5208, -3266, -2349, +1378, +1910, -2296, -1381, +715, -370, -1038, +545, -912, -20, -1028, -1437, +31, -67, -319, +18, +102, -464, },
|
||||
{ +18833, +12668, -5683, +2184, -2439, +1329, +4171, -4328, -5232, +3901, +4824, -4546, -3741, -3476, +1105, +2561, -1914, -1779, +533, -418, -1036, +485, -1184, +14, -859, -1412, +1, +27, -315, +91, +5, -584, },
|
||||
{ +19008, +13162, -5318, +1926, -2788, +1224, +4180, -3950, -5962, +3321, +5549, -3543, -3998, -4445, +848, +2902, -1193, -2106, +263, -305, -1180, +327, -1267, +112, -828, -1285, -169, +16, -197, +33, -71, -637, },
|
||||
{ +18737, +13681, -4618, +1963, -3145, +1044, +4007, -3789, -6476, +2407, +5985, -2289, -3707, -5492, +344, +3198, -328, -2230, -228, -74, -1401, +254, -1150, +181, -810, -1310, -322, -25, -144, -179, +21, -629, },
|
||||
{ +18348, +13822, -3468, +2221, -3496, +867, +3623, -3756, -6961, +1517, +5968, -1098, -2716, -6475, -416, +3438, +367, -2015, -778, +7, -1351, +154, -986, +288, -914, -1395, -403, -225, -85, -331, +78, -475, },
|
||||
{ +17731, +13756, -2024, +2354, -3509, +578, +3074, -3309, -7685, +628, +5586, -260, -1146, -7037, -1592, +3604, +681, -1553, -946, -177, -966, +4, -1088, +448, -897, -1594, -473, -493, -35, -196, +111, -266, },
|
||||
{ +16988, +13672, -858, +2219, -2857, +446, +2261, -2723, -8186, -143, +5173, +8, +354, -6856, -3083, +3505, +877, -1058, -660, -442, -746, +8, -1186, +419, -932, -1761, -398, -679, +86, +39, +115, -97, },
|
||||
{ +16138, +12803, +575, +2903, -2738, +274, +1624, -2112, -8167, -1458, +4911, +239, +1463, -6034, -4971, +3030, +1336, -611, -295, -547, -554, +6, -1339, +184, -852, -1914, -351, -543, +76, +333, +176, -169, },
|
||||
{ +15265, +12532, +737, +3734, -2365, -148, +1753, -2212, -7519, -2381, +4804, +154, +1395, -4336, -6360, +1992, +1942, -242, -104, -546, -503, +190, -1457, -473, -403, -1767, -438, -339, +44, +430, +96, -165, },
|
||||
{ +14708, +12215, +282, +4265, -1521, -1002, +1719, -1840, -6421, -2928, +4336, +115, +445, -2293, -6587, +102, +2286, +140, +231, -302, -617, -45, -1479, -669, -78, -1505, -452, -92, -181, +382, +108, -202, },
|
||||
{ +13989, +12100, -300, +4352, -584, -894, +751, -1879, -4250, -3597, +3863, +484, -1133, -527, -5742, -1882, +2289, +370, +107, +366, -557, -756, -1254, -675, +260, -958, -884, -49, -127, +270, +110, -168, },
|
||||
{ +13107, +11235, -316, +4649, +46, -886, +387, -2126, -3083, -3291, +3067, +1077, -1914, +82, -4188, -3391, +1793, +547, -349, +600, -246, -893, -1266, -562, +499, -574, -1293, -81, +40, +122, +177, -141, },
|
||||
{ +12123, +10207, -26, +5040, +240, -1159, +554, -1339, -3049, -3489, +2834, +1553, -2088, +86, -2702, -3839, +1078, +690, -758, +264, -223, -525, -820, -718, +257, -322, -1226, +21, +75, +12, +282, -234, },
|
||||
{ +10747, +9168, +566, +5397, +351, -1371, +926, -546, -2786, -3752, +1902, +1979, -1557, -9, -1749, -3706, +668, +745, -1061, -69, -419, -453, -245, -659, -216, -192, -848, +155, +137, -64, +205, -239, },
|
||||
{ +10338, +7957, +561, +6771, -2, -2128, +1652, +306, -2773, -4474, +1333, +2284, -1324, +617, -1488, -4429, +966, +918, -1497, -98, -822, -796, -54, -649, -27, -96, -951, +551, +331, -223, +48, -268, },
|
||||
{ +8720, +7871, +1937, +5955, -252, -2274, +1362, -265, -1942, -3385, -195, +1599, -597, +942, -704, -3945, -437, +923, -232, -93, -938, -655, -8, -340, +19, -166, -798, +69, +242, -236, +6, -124, },
|
||||
{ +7602, +7369, +1980, +5375, +188, -1525, +1271, -440, -1548, -3046, -447, +1237, -466, +757, -218, -3077, -861, +1080, +8, -496, -751, -394, -12, -415, -286, -141, -377, -40, -114, -262, +138, -57, },
|
||||
{ +6659, +6622, +2031, +4526, +599, -537, +864, -488, -1252, -2664, -491, +837, -265, +650, -6, -2474, -736, +1187, -23, -536, -523, -192, -58, -543, -514, -195, -232, -228, -227, -106, +203, +4, },
|
||||
{ +5900, +6258, +1847, +3648, +1138, +55, +581, -518, -1216, -2232, -426, +663, -175, +546, +190, -2072, -582, +1218, -57, -487, -330, -182, -121, -530, -618, -248, -293, -250, -52, -43, +131, +41, },
|
||||
{ +5056, +5699, +1640, +3073, +1682, +223, +366, -415, -1093, -1701, -428, +436, +76, +685, +156, -1697, -250, +1101, -72, -482, -271, -35, -164, -545, -578, -155, -255, -314, +31, +57, +129, +2, },
|
||||
{ +4420, +5031, +1526, +2971, +1795, +154, +317, -287, -871, -1279, -467, +257, +332, +803, +167, -1309, -82, +802, -141, -342, -151, -34, -185, -411, -382, -26, -278, -370, +32, +101, +150, -29, },
|
||||
{ +3741, +4414, +1550, +2755, +1697, +240, +358, -209, -641, -963, -482, +156, +636, +955, +153, -952, -130, +526, -22, -234, -141, -29, -17, -156, -225, -73, -393, -324, +91, +81, +127, +14, },
|
||||
{ +2974, +3771, +1714, +2268, +1466, +609, +473, -195, -532, -642, -208, +371, +868, +928, +168, -618, -68, +350, +7, -152, -30, +198, +164, -106, -265, -109, -239, -204, +88, +59, +101, +117, },
|
||||
{ +2091, +3036, +1907, +2129, +1391, +724, +464, -41, -262, -310, +40, +365, +939, +1032, +282, -384, -88, +174, -12, -37, +129, +229, +41, -48, -98, -70, -190, -179, +36, +72, +111, +99, },
|
||||
{ +1445, +2433, +1850, +1781, +1171, +805, +720, +119, +17, +232, +387, +551, +917, +1092, +388, -310, -142, -4, +121, +154, +38, +139, +164, +94, -72, -106, -103, -116, +22, +112, +121, +28, },
|
||||
{ +1185, +1993, +1603, +1441, +986, +909, +956, +692, +298, +365, +753, +730, +851, +889, +295, -301, -75, +80, +163, +139, -24, +148, +231, +110, -113, -59, -17, -3, +88, +93, +26, -171, },
|
||||
{ +929, +1455, +1263, +1389, +1207, +1075, +1117, +1195, +937, +623, +608, +551, +588, +631, +253, -42, +158, +103, +4, +39, +49, +61, +131, +212, +119, +81, -26, +0, +138, -59, -235, -232, },
|
||||
{ +822, +1583, +1506, +1563, +1699, +1350, +1018, +900, +685, +649, +736, +432, +188, +378, +360, +160, +134, -13, -136, -75, +123, +246, +203, +257, +240, +33, -106, -86, -100, -185, -187, -131, },
|
||||
{ +1055, +2056, +2052, +1866, +1431, +1170, +1157, +723, +299, +248, +365, +522, +438, +367, +368, +108, -145, -117, +92, +69, +74, +249, +238, +249, +240, +27, -128, -200, -286, -220, -88, -53, },
|
||||
{ +1205, +2171, +1980, +1773, +1441, +1300, +1201, +725, +193, -127, +142, +624, +550, +387, +335, +108, +110, +159, +2, -95, +146, +227, +29, +177, +403, +337, -41, -376, -330, -171, -140, -61, },
|
||||
{ +1241, +2158, +1760, +1464, +1328, +1398, +1227, +684, +95, +70, +568, +408, +149, +304, +228, +314, +526, +203, +21, +102, +15, -27, +94, +161, +134, +344, +341, -75, -343, -352, -143, -62, },
|
||||
{ +1622, +2444, +1530, +1228, +926, +1008, +1151, +744, -34, -42, +764, +765, +377, -38, -176, +304, +572, +431, +399, +163, +15, +139, +7, -83, +139, +174, +132, +311, +140, -144, -288, -260, },
|
||||
{ +1988, +2810, +1493, +1199, +762, +896, +984, +329, -524, -369, +974, +1088, +421, +50, +39, +109, +262, +367, +396, +394, +294, +181, +60, +66, -63, -115, +123, +273, +319, +228, -43, -271, },
|
||||
{ +2526, +3457, +1538, +1318, +724, +747, +744, -154, -1107, -804, +1097, +1060, +234, +52, +56, +324, +531, +217, +162, +419, +225, +256, +263, +67, +92, -2, -93, +88, +240, +428, +251, -76, },
|
||||
{ +3196, +4012, +1429, +1473, +720, +771, +686, -545, -1618, -1131, +1253, +952, -133, -206, -51, +422, +646, +293, +343, +345, +85, +287, +111, +124, +185, +14, -5, +226, +158, +71, +246, +198, },
|
||||
{ +4055, +4548, +1209, +1653, +679, +876, +676, -915, -2105, -1296, +1540, +778, -521, -439, -221, +267, +627, +274, +404, +557, +82, +190, +96, +57, +173, +67, +122, +268, +75, +91, +69, +85, },
|
||||
{ +5274, +5182, +788, +1972, +519, +928, +689, -1324, -2574, -1398, +1954, +556, -880, -663, -380, +149, +471, +122, +381, +459, +18, +339, +145, +8, +122, +110, +27, +301, +132, +67, +132, +6, },
|
||||
{ +6463, +5753, +204, +2322, +409, +883, +685, -1827, -2887, -1237, +2299, +272, -1164, -830, -425, +53, +394, -18, +257, +435, -217, +313, +174, +118, +26, -101, +42, +280, +156, +192, +176, +19, },
|
||||
{ +7727, +6595, -464, +2628, +421, +701, +721, -2439, -3227, -1064, +2569, +149, -1378, -1132, -432, +91, +148, +15, +160, +263, -252, +291, +71, -34, -78, -110, -65, +186, +193, +201, +372, +187, },
|
||||
{ +8674, +7441, -887, +2687, +475, +615, +762, -2918, -3490, -885, +2619, +32, -1422, -1271, -474, +50, +151, -43, +133, +383, -345, +103, -122, +5, -192, -218, -4, +192, +197, +219, +350, +56, },
|
||||
{ +9426, +8468, -909, +2618, +320, +470, +937, -3379, -3580, -906, +2153, +153, -1146, -1581, -641, -54, +224, +390, -97, +193, -307, +44, -287, -162, -284, -252, +7, +204, +290, +112, +287, +9, },
|
||||
{ +9648, +9372, -545, +2609, +289, -35, +1177, -3423, -3509, -971, +1312, +111, -652, -1474, -971, -83, +171, +613, -45, +26, -344, -79, -230, -165, -334, -249, -69, -50, +303, +195, +309, -45, },
|
||||
{ +9794, +9998, +39, +2769, +379, -638, +980, -3266, -3261, -859, +205, -83, -237, -1162, -748, -518, -59, +946, +29, -268, -354, +5, -131, -183, -422, -257, -155, -194, +224, +120, +248, +7, },
|
||||
{ +9981, +10397, +622, +3286, +610, -1141, +509, -3403, -3000, -195, -811, -998, +359, -548, -477, -751, -558, +952, +151, -164, -95, -136, -277, -86, -487, -322, -168, -469, +127, +155, +238, -39, },
|
||||
{ +10400, +10438, +745, +3939, +894, -1363, +3, -3769, -2585, +471, -1670, -1614, +581, -153, +52, -948, -1056, +930, +424, -236, -31, +17, -267, -241, -666, -379, -196, -563, -20, +129, +269, +14, },
|
||||
{ +11293, +10206, +442, +4923, +1094, -1505, -530, -3996, -2130, +1419, -2437, -2806, +924, +121, +806, -871, -1571, +1001, +618, -424, +58, +151, -379, -185, -956, -481, -127, -651, -119, +140, +160, -149, },
|
||||
{ +12405, +9835, -281, +5828, +885, -1582, -583, -4592, -1654, +2615, -2996, -4362, +959, +504, +1380, -564, -2020, +895, +761, -610, +81, +185, -720, -204, -1012, -549, -116, -759, -235, +116, +85, -201, },
|
||||
{ +13934, +9778, -1546, +6319, +686, -1381, -382, -5271, -1119, +3944, -3172, -5767, +686, +682, +1741, +77, -2181, +507, +979, -721, -237, +318, -773, -227, -1265, -756, +92, -716, -477, +162, +134, -400, },
|
||||
{ +15591, +10583, -3448, +5772, +592, -1315, +577, -5494, -1701, +4790, -2265, -6627, -93, +745, +2160, +357, -2547, +377, +1130, -881, -407, +529, -934, -349, -1319, -885, +105, -673, -507, +45, +171, -344, },
|
||||
{ +17313, +11801, -5808, +4851, +227, -980, +2385, -6012, -2746, +5567, -996, -7076, -952, +586, +2154, +773, -2812, -169, +1297, -600, -709, +461, -948, -242, -1337, -1246, +226, -600, -658, -10, +354, -281, },
|
||||
{ +19637, +11754, -7792, +4845, -732, -208, +3348, -6604, -2889, +6378, +241, -7587, -1944, +32, +2714, +992, -3500, -540, +1647, -590, -1131, +670, -931, -134, -1354, -1421, +144, -583, -536, +136, +307, -469, },
|
||||
{ +21056, +11745, -8787, +4783, -1622, +469, +3944, -6753, -3241, +6740, +1590, -8058, -2609, -411, +2588, +1199, -3554, -896, +1543, -588, -1309, +826, -1017, +54, -1317, -1707, +140, -326, -455, +92, +212, -518, },
|
||||
{ +22470, +11991, -9701, +4389, -2684, +1431, +4782, -6942, -4015, +6909, +3084, -7923, -3158, -1809, +2522, +1950, -3598, -1289, +1296, -609, -1331, +920, -1125, +160, -1425, -1754, +339, -152, -445, +42, +154, -583, },
|
||||
{ +23333, +12284, -9893, +3816, -3344, +2109, +5145, -6669, -4930, +6673, +4509, -7499, -3802, -2971, +2448, +2729, -3461, -1700, +1131, -635, -1249, +918, -1341, +144, -1300, -1646, +329, -23, -418, +82, +75, -652, },
|
||||
{ +23907, +12687, -9670, +3149, -3819, +2297, +5278, -6129, -6015, +6230, +5855, -6823, -4412, -4207, +2246, +3493, -3012, -2245, +989, -670, -1278, +815, -1685, +265, -1083, -1694, +279, +79, -444, +187, -65, -803, },
|
||||
{ +24068, +13242, -9317, +2853, -4157, +2181, +5293, -5711, -6874, +5677, +6685, -5741, -4828, -5227, +2103, +3851, -2196, -2705, +724, -483, -1476, +639, -1724, +395, -1100, -1511, +43, +72, -276, +84, -146, -836, },
|
||||
{ +23835, +13868, -8722, +2737, -4517, +2057, +5139, -5437, -7512, +4767, +7374, -4441, -4873, -6362, +1764, +4214, -1258, -3060, +266, -145, -1812, +589, -1602, +416, -1030, -1500, -192, +122, -223, -168, -12, -862, },
|
||||
{ +23307, +14493, -7616, +2886, -5061, +1805, +4905, -5438, -8024, +3633, +7772, -2910, -4088, -7840, +1069, +4716, -316, -3023, -501, +151, -1943, +531, -1399, +596, -1194, -1653, -168, -131, -123, -407, +123, -804, },
|
||||
{ +22971, +14195, -6115, +3007, -5341, +1777, +4268, -5238, -8637, +2925, +7500, -1787, -2907, -8792, +313, +4922, +197, -2731, -1003, +162, -1684, +337, -1204, +594, -1286, -1645, -381, -303, -37, -516, +148, -504, },
|
||||
{ +22070, +14375, -4392, +2868, -5223, +1286, +3714, -4447, -9674, +1907, +7159, -860, -973, -9579, -1209, +5344, +421, -2142, -1115, -130, -1112, +96, -1456, +898, -1239, -2004, -343, -667, +59, -231, +163, -308, },
|
||||
{ +21240, +14393, -3174, +2492, -4298, +1094, +2745, -3688, -10265, +1178, +6777, -618, +645, -9443, -2927, +5381, +636, -1617, -791, -478, -882, +115, -1535, +816, -1293, -2149, -223, -878, +246, +6, +122, -102, },
|
||||
{ +20341, +13345, -1099, +2866, -4191, +914, +1804, -2626, -10582, -327, +6635, -287, +2111, -8876, -5214, +5209, +1109, -1120, -336, -736, -608, +24, -1715, +729, -1303, -2434, -80, -706, +170, +364, +230, -221, },
|
||||
{ +19164, +13059, -468, +4105, -4326, +617, +1911, -2785, -9817, -1770, +6595, -131, +2540, -7381, -7331, +4587, +2019, -857, -311, -599, -501, +196, -1861, -41, -744, -2295, -231, -641, +296, +624, -2, -206, },
|
||||
{ +18422, +13001, -808, +4597, -3422, -211, +2249, -2884, -9087, -2495, +6668, -543, +1814, -4858, -8725, +3134, +2718, -437, +54, -836, -508, +440, -2036, -713, -94, -2244, -301, -159, -24, +549, +64, -190, },
|
||||
{ +17855, +12583, -1366, +5362, -2553, -1443, +2178, -2151, -7613, -3290, +5904, -270, +528, -2457, -8756, +644, +3333, -90, +363, -320, -761, -106, -1922, -663, +147, -1870, -338, +6, -294, +558, +55, -269, },
|
||||
{ +16989, +12653, -2050, +5407, -1459, -1174, +828, -2193, -4917, -4430, +5563, +243, -1454, -249, -7905, -1684, +3547, +136, +95, +521, -741, -985, -1507, -732, +567, -1249, -1016, +108, -174, +393, +77, -205, },
|
||||
{ +15955, +11844, -2035, +5709, -643, -1173, +284, -2723, -3123, -4229, +4384, +1201, -2654, +769, -6230, -3909, +3368, +375, -503, +1015, -370, -1372, -1549, -501, +871, -801, -1619, +110, +41, +191, +164, -153, },
|
||||
{ +15076, +10787, -1651, +6161, -494, -1528, +363, -1853, -3374, -4091, +4325, +1345, -2786, +774, -4553, -4709, +2401, +708, -854, +734, -248, -874, -1363, -746, +954, -560, -1815, +201, +149, +40, +297, -250, },
|
||||
{ +13619, +9790, -1075, +6480, -296, -1830, +805, -986, -3179, -4836, +3833, +2526, -3239, +844, -2857, -5293, +1983, +856, -1538, +348, -255, -582, -521, -965, +224, -130, -1436, +334, +56, -57, +433, -405, },
|
||||
{ +11990, +8803, -122, +6797, -290, -2053, +1262, -151, -2986, -4897, +2471, +2765, -2059, +507, -2148, -4881, +1467, +956, -1677, +43, -545, -596, -9, -842, -196, -59, -1007, +415, +207, -148, +219, -297, },
|
||||
{ +12081, +7961, -53, +8316, -2153, -3098, +1599, -1374, -4780, -3918, +6091, +1729, -1658, +3619, -1653, -4298, +1906, +240, -2274, -904, -1646, -475, -581, -1417, -48, -621, -760, +583, -70, -397, +372, +38, },
|
||||
{ +10221, +7710, +913, +7396, -744, -3594, +288, -1159, -3490, -2578, +4228, +2100, -676, +2348, -1090, -3898, +283, +166, -1419, -166, -1389, -451, +154, -1605, +30, -242, -1341, +92, +30, -175, +146, +10, },
|
||||
{ +8688, +6964, +1507, +6729, -314, -2703, +315, -1206, -2584, -2071, +3004, +1882, -602, +1784, -753, -3344, +27, -28, -1040, +36, -1038, -169, +101, -1356, +51, -283, -1249, -154, -166, -180, +96, -77, },
|
||||
{ +7115, +6222, +2194, +6013, +21, -1813, +346, -984, -1850, -1758, +2164, +1394, -547, +1436, -539, -2734, -356, -16, -801, -44, -393, +279, +133, -1115, -80, -420, -1086, -375, -460, -339, +102, +64, },
|
||||
{ +5945, +5731, +2369, +5160, +628, -1311, +172, -355, -1302, -1644, +1506, +1052, -512, +980, -248, -2308, -600, +61, -662, +216, +230, +430, +21, -924, -262, -525, -975, -595, -515, -352, +24, +58, },
|
||||
{ +4951, +5289, +2362, +4475, +1066, -1021, +399, +107, -1048, -1369, +919, +662, -450, +769, -110, -1957, -639, +150, -163, +531, +305, +258, +28, -774, -421, -578, -897, -479, -486, -369, -53, -38, },
|
||||
{ +4363, +4902, +2175, +3906, +1203, -661, +681, +254, -736, -1138, +362, +420, -286, +700, -78, -1616, -376, +371, +225, +620, +133, +107, -31, -735, -442, -346, -613, -527, -506, -305, -33, +11, },
|
||||
{ +3887, +4410, +1997, +3432, +1166, -200, +836, +280, -488, -1038, -68, +289, -94, +640, +127, -1246, -284, +556, +413, +412, -66, +15, -158, -547, -132, -220, -649, -561, -395, -212, +21, +92, },
|
||||
{ +3376, +3956, +1885, +2975, +1204, +193, +869, +327, -309, -968, -279, +153, +258, +942, +175, -1108, -255, +727, +480, +101, -239, +78, +89, -338, -137, -200, -570, -519, -230, -33, +148, +121, },
|
||||
{ +2882, +3708, +1714, +2272, +1421, +628, +806, +292, -242, -915, -154, +676, +462, +684, +167, -864, -170, +750, +347, -82, -16, +193, +144, -210, -259, -192, -347, -281, -16, +141, -8, -250, },
|
||||
{ +2046, +3121, +1820, +2057, +1654, +693, +650, +385, +140, -232, -150, +593, +684, +440, +155, -506, -275, +602, +454, -11, +56, +206, +86, -187, -110, +2, -156, -45, +18, -158, -271, -285, },
|
||||
{ +1488, +2866, +1983, +1297, +1310, +1249, +896, +455, +406, +318, +200, +433, +517, +126, +107, -107, -188, +504, +467, +57, +7, +98, +167, +102, +54, +69, +57, -187, -324, -266, -204, -157, },
|
||||
{ +1053, +2185, +1754, +1283, +1374, +1384, +1276, +1018, +579, +445, +413, +125, +270, +305, -100, -39, +315, +357, +242, +93, +19, +226, +393, +295, +143, -27, -218, -292, -266, -226, -155, -132, },
|
||||
{ +1057, +1688, +1471, +1786, +1766, +1650, +1060, +783, +933, +472, +248, +183, +76, +69, +137, +183, +238, +233, +230, +317, +365, +313, +253, +237, +26, -223, -314, -259, -170, -175, -164, -190, },
|
||||
{ +1484, +2348, +1611, +1846, +1758, +1472, +1102, +423, +268, +233, +403, +262, +27, +176, +309, +97, -27, +318, +563, +614, +474, +195, -31, -203, -192, -193, -175, -172, -180, -189, -226, -227, },
|
||||
{ +1189, +2150, +2013, +2091, +1722, +1365, +885, +462, +260, +102, +413, +490, +320, +96, +1, +58, +212, +334, +486, +716, +577, +224, -92, -268, -289, -245, -180, -118, -126, -201, -214, -200, },
|
||||
{ +1538, +2422, +1826, +1858, +1328, +1049, +982, +559, +11, +288, +803, +359, +369, +46, -142, +244, +201, +241, +677, +674, +409, +408, -39, -390, -282, -219, -225, -163, -112, -102, -144, -262, },
|
||||
{ +1451, +2242, +1550, +1542, +1259, +1129, +1099, +613, +12, +373, +659, +432, +541, +134, +290, +219, +14, +256, +292, +393, +633, +888, +287, -211, -275, -432, -238, -88, -138, -127, -59, -184, },
|
||||
{ +1889, +2552, +1389, +1386, +798, +799, +1037, +440, -97, +313, +902, +615, +250, +224, +305, +196, +454, +188, +213, +467, +218, +428, +693, +519, -184, -445, -344, -230, -88, -89, -79, -137, },
|
||||
{ +2363, +3156, +1398, +1195, +447, +518, +860, +263, -393, +80, +1073, +455, +343, +419, -4, +307, +535, +250, +536, +315, -34, +496, +401, +321, +495, -3, -404, -292, -153, -89, -99, -128, },
|
||||
{ +2825, +3421, +1231, +1466, +685, +599, +704, -461, -1098, +100, +1264, +329, +295, +180, +24, +419, +226, +386, +756, +380, +127, +240, +62, +328, +348, +131, +263, +30, -341, -335, -68, -108, },
|
||||
{ +3491, +4043, +1102, +1542, +710, +589, +566, -937, -1530, +137, +1469, +122, -70, -161, -43, +468, +450, +426, +430, +401, +160, +372, +284, +90, +60, +56, +234, +293, +163, -117, -305, -271, },
|
||||
{ +4318, +4870, +895, +1479, +603, +516, +626, -1326, -2005, +237, +1656, -115, -219, -418, -328, +396, +406, +377, +591, +338, -178, +469, +312, +158, +217, -234, -8, +329, +210, +69, +187, -16, },
|
||||
{ +5303, +5497, +566, +1592, +649, +557, +509, -1916, -2395, +604, +1814, -332, -334, -762, -437, +361, +208, +350, +554, +119, -140, +409, +18, +313, +179, -239, +98, -40, +40, +299, +419, +154, },
|
||||
{ +6339, +6214, +236, +1676, +646, +546, +369, -2580, -2680, +1136, +1943, -586, -423, -1111, -529, +411, +162, +286, +376, -36, -255, +422, +29, +77, +2, -290, -43, +175, +135, +31, +367, +299, },
|
||||
{ +7425, +6991, -61, +1742, +482, +489, +257, -3260, -2805, +1599, +1929, -688, -450, -1459, -633, +395, +140, +359, +251, -272, -354, +408, -47, +55, -274, -528, -51, +174, +73, +63, +482, +141, },
|
||||
{ +8275, +7928, -159, +1756, +330, +237, +226, -3790, -2846, +1890, +1754, -690, -422, -1605, -800, +248, +185, +442, +143, -377, -482, +428, -180, -145, -222, -605, -186, -18, +24, +140, +387, +163, },
|
||||
{ +8779, +8969, +132, +1669, +354, -215, +82, -3976, -2837, +1984, +1471, -648, -422, -1570, -842, -60, +130, +630, +84, -403, -597, +177, +30, -221, -403, -610, -358, +87, -64, -83, +409, +63, },
|
||||
{ +8644, +10014, +1089, +1453, +493, -603, -298, -3906, -2820, +1754, +1240, -552, -546, -1453, -849, -345, +44, +672, +148, -531, -682, +196, +32, -233, -584, -609, -409, -84, -103, -217, +378, +199, },
|
||||
{ +8306, +10593, +2458, +1598, +488, -758, -657, -3861, -2849, +1369, +1000, -322, -680, -1443, -722, -668, -37, +677, -45, -127, -673, -156, +70, -281, -443, -635, -631, -315, -160, -54, +479, +139, },
|
||||
{ +8399, +10550, +3351, +2403, +311, -794, -759, -3852, -2757, +792, +477, +47, -726, -1554, -558, -902, -349, +816, +82, -285, -484, -159, +82, -356, -623, -644, -824, -243, +7, -98, +292, +151, },
|
||||
{ +8816, +10302, +3569, +3594, +382, -1029, -763, -3770, -2393, +191, -597, +339, -557, -1492, -281, -1488, -279, +1005, -143, -84, -408, -266, +120, -345, -812, -549, -772, -407, -193, -80, +533, +172, },
|
||||
{ +9548, +9994, +3253, +4643, +623, -1187, -749, -3597, -1800, -143, -2200, +304, -135, -1804, +254, -1488, -709, +1187, -140, -38, -318, -470, +133, -281, -894, -381, -819, -627, -170, -88, +384, +19, },
|
||||
{ +10751, +9616, +2488, +5649, +553, -1161, -620, -3463, -848, -358, -3996, +121, +130, -2080, +871, -1563, -750, +1416, -465, -119, -39, -417, +70, -275, -1074, -309, -632, -645, -410, -224, +390, -12, },
|
||||
{ +12014, +9312, +1365, +6755, +300, -1330, -113, -3321, +199, -72, -5935, -749, +987, -2491, +1095, -1087, -1039, +1478, -609, -197, +278, -296, -103, -265, -1088, -288, -651, -818, -474, -102, +420, -351, },
|
||||
{ +13420, +9342, -277, +7865, +178, -2087, +567, -3307, +1525, +754, -7969, -2060, +1804, -2289, +1011, -875, -1158, +1512, -570, -399, +356, -20, -88, -202, -1219, -576, -573, -671, -574, -252, +223, -435, },
|
||||
{ +14807, +9961, -2295, +8150, +567, -2864, +1147, -3390, +2627, +2008, -9297, -3776, +2200, -1711, +807, -518, -1276, +1360, -541, -609, +222, +438, +75, -495, -1315, -653, -426, -658, -1054, -304, +558, -512, },
|
||||
{ +16799, +11079, -4722, +7037, +966, -2929, +1994, -3796, +2704, +3553, -9595, -5358, +1866, -912, +909, -200, -1483, +979, -455, -806, +207, +669, +68, -725, -1243, -745, -665, -630, -1077, -219, +691, -658, },
|
||||
{ +18748, +11700, -6587, +6122, +943, -2914, +2664, -4426, +2804, +5554, -10085, -6616, +1486, -372, +1328, -285, -1517, +471, -396, -837, +75, +587, +104, -751, -1461, -967, -816, -312, -988, -264, +684, -677, },
|
||||
{ +20863, +12866, -8572, +4385, +266, -1602, +3832, -5716, +2704, +6971, -9876, -6842, +1023, -479, +1689, -35, -1641, +123, -340, -939, -107, +662, +89, -932, -1675, -1081, -768, +37, -888, -310, +796, -570, },
|
||||
{ +22765, +13783, -10202, +2941, -746, -81, +4256, -6846, +2898, +7803, -8917, -6979, +238, -972, +2079, +576, -1912, -370, -306, -915, -363, +634, -131, -948, -1776, -1303, -718, +235, -798, -279, +1030, -691, },
|
||||
{ +24585, +14272, -11287, +1831, -1784, +1157, +4160, -7450, +3079, +8440, -7630, -7341, -269, -1716, +2654, +1114, -2449, -581, -181, -975, -680, +550, -325, -686, -1945, -1634, -616, +402, -659, -135, +1021, -867, },
|
||||
{ +25869, +14987, -12073, +934, -2610, +1696, +3918, -7361, +2532, +9076, -6029, -7754, -590, -2620, +3019, +1624, -2920, -652, -46, -1264, -966, +600, -472, -467, -2153, -1908, -503, +517, -451, -126, +848, -860, },
|
||||
{ +26886, +15454, -12309, +371, -3390, +1840, +3592, -7055, +1669, +9594, -4128, -8018, -1017, -3459, +3063, +2189, -3137, -722, +61, -1655, -1121, +760, -729, -326, -2158, -2099, -462, +648, -404, -166, +686, -817, },
|
||||
{ +27684, +15653, -11980, +24, -4099, +1713, +3246, -6955, +802, +10113, -2356, -7981, -1587, -4226, +3032, +2573, -2987, -885, +150, -1954, -1167, +794, -988, -168, -2014, -2167, -434, +666, -569, -144, +540, -855, },
|
||||
{ +27741, +16216, -11243, -193, -4856, +1352, +3238, -7293, -125, +10598, -852, -7518, -2157, -4853, +2670, +2868, -2470, -1300, +205, -1901, -1416, +672, -1092, +16, -1804, -2095, -591, +514, -688, -235, +476, -797, },
|
||||
{ +27272, +16910, -10056, -159, -5917, +966, +3706, -8117, -996, +10790, +336, -6433, -2511, -5475, +1946, +3049, -1685, -1717, +71, -1629, -1727, +569, -1061, +184, -1403, -2279, -872, +450, -920, -127, +585, -861, },
|
||||
{ +26292, +17598, -8440, +25, -7051, +509, +4314, -9076, -1994, +10815, +1175, -4930, -2295, -6533, +1145, +3192, -1111, -1763, -328, -1487, -1778, +538, -867, +358, -1413, -2301, -1193, +94, -600, -21, +592, -845, },
|
||||
{ +24908, +18211, -6510, +265, -7874, +12, +4724, -9478, -3509, +10716, +2192, -3778, -1219, -7880, +248, +3471, -999, -1541, -792, -1628, -1421, +841, -1035, +457, -1363, -2591, -1213, -67, -308, +27, +657, -759, },
|
||||
{ +23173, +18778, -4279, +420, -8200, -952, +4606, -8583, -5614, +10055, +3892, -3343, +694, -8935, -1196, +3813, -1288, -1146, -860, -1990, -933, +977, -1325, +807, -1645, -2747, -714, -417, -75, +218, +548, -805, },
|
||||
{ +22000, +18365, -2003, +631, -8453, -1561, +3477, -6891, -7183, +8672, +5645, -3122, +2529, -8996, -3276, +3955, -1630, -956, -275, -2341, -1164, +1321, -1481, +629, -1309, -2933, -330, -280, -173, +221, +483, -718, },
|
||||
{ +21112, +17584, -502, +1334, -8158, -2273, +2292, -5787, -7662, +7007, +7216, -2614, +3372, -7775, -5664, +3835, -1591, -1170, +244, -2374, -1158, +1090, -1550, +627, -758, -3019, +93, -274, -410, +438, +348, -800, },
|
||||
{ +20632, +17088, -637, +2364, -7070, -3739, +1695, -4935, -8217, +5848, +8673, -2173, +3453, -6082, -7678, +3590, -1152, -1978, +580, -2279, -1275, +1124, -1750, +530, -17, -2783, -114, -166, -296, +284, +223, -679, },
|
||||
{ +20783, +15919, -1263, +4036, -6965, -4754, +1892, -4346, -8539, +4539, +9671, -1610, +3670, -4797, -8663, +2624, -468, -2009, +28, -2355, -1460, +1576, -1563, +142, +565, -2599, -176, -95, -480, +333, +227, -736, },
|
||||
{ +19898, +14834, -883, +5611, -7741, -5058, +2778, -4286, -8109, +2022, +9952, -268, +3452, -2598, -9652, +1195, +498, -1859, -458, -2733, -1925, +1990, -998, -244, +1156, -2794, -697, +541, -558, +197, +87, -769, },
|
||||
{ +18353, +13734, -294, +6773, -7182, -5247, +2895, -3546, -8000, -248, +9510, +1071, +3101, -432, -9279, -621, +1397, -1978, -692, -2518, -2682, +1633, -584, +363, +1428, -3524, -1018, +1348, -563, -124, +38, -581, },
|
||||
{ +17879, +12019, -1720, +8332, -4976, -5136, +2334, -3753, -7617, -1708, +9554, +1908, +1367, +1443, -7575, -1992, +1783, -1645, -1254, -2425, -2861, +887, -188, +457, +1489, -3426, -1450, +1731, -415, -317, +8, -369, },
|
||||
{ +16652, +10730, -1605, +9069, -4100, -5114, +2342, -2722, -7212, -3184, +9140, +2114, -706, +3732, -5117, -4057, +2505, -1340, -2078, -1590, -2693, +151, -496, -93, +1733, -2286, -1942, +1173, +71, -202, +59, -356, },
|
||||
{ +15156, +9682, -934, +8954, -3661, -4339, +2035, -2043, -6405, -3988, +8467, +1700, -1521, +4564, -3502, -4488, +2506, -991, -2469, -1109, -2320, -118, -899, -916, +1437, -1210, -1567, +612, -176, -55, +492, -295, },
|
||||
{ +13768, +8771, -687, +8850, -3021, -3693, +1828, -1815, -5404, -4221, +7310, +1847, -1818, +4247, -2573, -4214, +2383, -697, -2275, -1002, -2080, -223, -830, -1342, +608, -827, -917, +655, -473, -208, +715, -175, },
|
||||
{ +14260, +8470, -4402, +9252, +637, -4904, +113, -702, -4629, -3590, +7432, -364, -3180, +3406, -815, -2084, +3326, +846, -1128, +174, -1402, -361, -2087, -2634, -411, -921, -1460, -69, -168, +123, +619, -314, },
|
||||
{ +12203, +8042, -2103, +8153, +36, -4209, +166, -837, -3973, -3102, +5672, +328, -1860, +2823, -972, -1947, +2514, +629, -644, +18, -1399, -247, -1603, -2116, -289, -1032, -1262, -81, -282, -111, +472, -81, },
|
||||
{ +9978, +7761, -851, +7167, +518, -3404, +616, -905, -3552, -2875, +4478, +661, -1312, +2289, -665, -1823, +1788, +811, -693, -46, -1135, -436, -1105, -1539, -267, -710, -1155, -219, -156, -293, +133, -138, },
|
||||
{ +8252, +7011, +325, +6475, +571, -2476, +1105, -925, -3370, -2437, +3577, +876, -1052, +1795, -250, -1885, +1251, +954, -652, -129, -973, -426, -862, -997, +85, -618, -1083, -201, -130, -385, -107, -293, },
|
||||
{ +6990, +6408, +794, +5771, +1007, -1732, +1092, -813, -2841, -2294, +2966, +1185, -1180, +1392, +18, -1807, +929, +855, -550, -195, -860, -345, -413, -570, +52, -503, -888, -152, -282, -552, -210, -339, },
|
||||
{ +6315, +5794, +807, +5067, +1552, -954, +511, -614, -2056, -2084, +2442, +1235, -1125, +916, +7, -1376, +711, +581, -457, -233, -563, +16, -83, -537, -90, -310, -669, -307, -465, -561, -130, -413, },
|
||||
{ +5495, +5067, +1235, +4594, +1625, -330, +383, -413, -1595, -1679, +1966, +934, -895, +616, -4, -1020, +465, +389, -292, +92, -212, +95, -14, -517, -157, -268, -599, -430, -445, -406, -157, -409, },
|
||||
{ +4664, +4577, +1540, +4071, +1781, +42, +302, -196, -1100, -1341, +1290, +763, -650, +297, +59, -796, +256, +526, +78, +88, -122, +237, -54, -604, -266, -281, -530, -328, -364, -356, -110, -313, },
|
||||
{ +3851, +4119, +1861, +3768, +1681, +225, +590, +32, -804, -1149, +865, +588, -638, +296, +218, -632, +437, +631, +5, +112, +96, +233, -258, -721, -220, -100, -390, -319, -297, -172, -5, -185, },
|
||||
{ +3336, +3750, +1919, +3488, +1574, +321, +813, +276, -570, -1058, +527, +383, -558, +511, +605, -444, +230, +571, +103, +183, +137, -15, -469, -450, -1, -70, -317, -262, -110, +31, -67, -427, },
|
||||
{ +3049, +3510, +1714, +3215, +1488, +383, +1012, +442, -405, -1074, +260, +303, -38, +921, +427, -524, +201, +517, +216, +135, -99, -116, -221, -179, +46, -34, -244, -78, -50, -204, -303, -502, },
|
||||
{ +2844, +3234, +1490, +2937, +1418, +496, +1224, +481, -359, -986, +230, +762, +253, +708, +333, -527, +155, +538, +90, +24, +59, +6, -45, -70, +130, +103, -256, -256, -271, -304, -297, -325, },
|
||||
{ +2647, +2909, +1294, +2675, +1363, +698, +1331, +460, -184, -366, +335, +522, +431, +628, +210, -521, -47, +509, +303, +218, +56, +23, +150, +170, +182, -200, -553, -341, -197, -165, -211, -392, },
|
||||
{ +2534, +2823, +1022, +1970, +1429, +1221, +1392, +703, +196, -392, +319, +726, +304, +316, -41, -517, +273, +763, +274, +109, +137, +366, +348, -105, -308, -391, -404, -192, -101, -238, -327, -356, },
|
||||
{ +1989, +2468, +1081, +1801, +1546, +1331, +1957, +1126, +247, -246, -18, +528, +333, +129, -70, -149, +414, +761, +386, +169, +318, +372, +30, -395, -334, -284, -295, -143, -195, -279, -332, -341, },
|
||||
{ +1620, +2490, +1471, +1655, +2013, +1585, +1301, +1138, +603, -161, -181, -90, -24, +524, +502, +148, +414, +612, +427, +246, +105, +30, -174, -325, -240, -291, -355, -203, -136, -260, -309, -267, },
|
||||
{ +1263, +2410, +2075, +1764, +1891, +1970, +1489, +641, +109, -195, -152, +183, +214, +240, +589, +646, +460, +466, +334, +34, -111, -86, -179, -264, -241, -257, -328, -326, -184, -161, -161, -211, },
|
||||
{ +1659, +2476, +1676, +2229, +1972, +1422, +1669, +708, -448, -272, +296, +223, +206, +469, +501, +694, +819, +331, -104, -205, -133, -45, -56, -253, -357, -286, -277, -167, -214, -215, -128, -196, },
|
||||
{ +1539, +2551, +1891, +1969, +1762, +1252, +1228, +862, +110, -20, +303, +230, +166, +358, +647, +849, +752, +128, -158, -145, -243, -82, -52, -123, -279, -382, -292, -181, -163, -170, -155, -213, },
|
||||
{ +1700, +2807, +1619, +1270, +1372, +1179, +1161, +1231, +504, +52, +504, +136, +92, +640, +736, +453, +675, +669, -252, -473, -51, +20, -170, -152, -154, -240, -345, -332, -109, -89, -138, -173, },
|
||||
{ +1694, +2657, +1452, +968, +771, +1023, +1613, +1199, +359, +667, +624, -76, +382, +536, +594, +753, +564, +532, +330, -84, -457, -257, +116, +14, -260, -224, -186, -331, -270, -123, -32, -136, },
|
||||
{ +1868, +2708, +1232, +929, +844, +827, +1033, +736, +424, +738, +823, +481, +179, +246, +857, +667, +444, +613, +612, +119, -110, -152, -501, -78, +116, -202, -290, -173, -262, -254, -131, -141, },
|
||||
{ +2010, +2795, +1111, +875, +1109, +1106, +857, -4, -401, +861, +1319, +394, +265, +408, +467, +655, +737, +455, +536, +705, +304, -158, -205, -280, -395, +24, +44, -233, -199, -207, -233, -158, },
|
||||
{ +2330, +2991, +964, +1027, +1208, +1180, +792, -487, -765, +646, +1124, +466, +294, +78, +501, +769, +452, +561, +675, +354, +514, +630, -115, -307, -312, -411, -176, +29, -28, -209, -223, -273, },
|
||||
{ +2824, +3565, +965, +943, +1112, +1083, +667, -717, -987, +738, +1121, +2, +110, +106, +412, +576, +458, +606, +513, +519, +535, +409, +396, +221, -406, -363, -190, -297, -93, +42, -95, -282, },
|
||||
{ +3349, +3983, +872, +1076, +1173, +1134, +531, -1288, -1216, +1017, +1171, -131, -155, -369, +349, +670, +365, +458, +453, +375, +399, +660, +331, +266, +204, -128, -309, -306, -200, -192, -42, -94, },
|
||||
{ +3961, +4442, +707, +1254, +1277, +1123, +361, -1769, -1366, +1301, +1136, -328, -227, -538, +131, +409, +403, +542, +281, +118, +332, +560, +285, +456, +66, +169, +247, -283, -261, -198, -228, -289, },
|
||||
{ +4686, +5052, +536, +1343, +1245, +1005, +207, -2188, -1265, +1645, +926, -588, -346, -640, +177, +285, +106, +548, +311, +27, +151, +303, +255, +325, +75, +299, +196, +155, +118, -260, -236, -300, },
|
||||
{ +5534, +5563, +318, +1536, +1252, +806, -127, -2646, -995, +2082, +649, -787, -525, -824, +252, +141, +115, +500, +81, -14, +111, +227, +28, +135, -85, +201, +321, +171, +203, +214, -19, -500, },
|
||||
{ +6386, +6175, +93, +1727, +1236, +541, -519, -3046, -559, +2421, +322, -926, -609, -965, +269, -57, +33, +671, -21, -197, +21, +165, +4, +40, -436, +106, +232, +49, +425, +144, +66, +0, },
|
||||
{ +7130, +6925, -88, +1879, +1277, +209, -950, -3419, -58, +2693, -41, -998, -710, -991, +321, -320, -82, +770, -64, -189, -88, -19, +13, -38, -523, -42, -30, +68, +150, +89, +365, -23, },
|
||||
{ +7792, +7702, -133, +1956, +1357, -128, -1415, -3772, +482, +2874, -375, -1048, -862, -936, +402, -512, -233, +760, -129, -149, -81, -155, -78, -100, -630, -36, -59, -442, +95, +280, +185, +75, },
|
||||
{ +8084, +8648, +147, +1800, +1588, -465, -1801, -4057, +739, +3060, -585, -1055, -1095, -889, +484, -597, -294, +651, -254, -145, -54, -182, -110, -282, -564, -209, -393, -202, +61, +14, +171, +1, },
|
||||
{ +7950, +9589, +964, +1429, +1845, -635, -2157, -4183, +602, +3177, -602, -1039, -1301, -965, +529, -614, -333, +613, -355, -295, -22, -253, -53, -255, -948, -322, -205, -347, -6, +31, +59, -71, },
|
||||
{ +7608, +10215, +2256, +1215, +1866, -509, -2499, -4150, +252, +3102, -462, -1007, -1377, -1131, +543, -620, -361, +628, -344, -407, -176, -143, -39, -636, -843, -265, -361, -355, -99, +51, +143, +45, },
|
||||
{ +7478, +10350, +3382, +1641, +1562, -364, -2671, -4182, +82, +2795, -422, -952, -1415, -1234, +496, -705, -369, +637, -288, -460, -139, -421, -273, -301, -1004, -427, -393, -387, -99, +121, +207, -128, },
|
||||
{ +7601, +10277, +3938, +2546, +1409, -546, -2604, -4195, -39, +2430, -446, -937, -1542, -1235, +449, -844, -397, +623, -239, -176, -592, -583, -41, -478, -953, -587, -540, -343, +32, +110, -10, -156, },
|
||||
{ +7999, +10060, +3982, +3415, +1699, -807, -2609, -3807, -190, +1946, -508, -758, -1860, -1306, +494, -1071, -466, +696, -160, -448, -342, -561, -316, -440, -936, -533, -717, -285, -33, -87, +138, +52, },
|
||||
{ +8868, +9667, +3696, +4220, +1884, -786, -2974, -3169, -25, +1022, -611, -333, -2076, -1587, +587, -1486, -187, +551, -545, +152, -427, -718, -238, -557, -971, -356, -742, -544, -186, +78, +286, -144, },
|
||||
{ +10008, +9228, +3019, +5173, +1730, -514, -3173, -2654, +674, -266, -982, +303, -2059, -1915, +478, -1617, -384, +327, -296, +313, -485, -639, -255, -549, -996, -197, -986, -652, +62, -23, +48, -97, },
|
||||
{ +11234, +8968, +1867, +6284, +1449, -576, -2850, -2320, +1671, -1457, -1966, +1061, -1777, -2251, +610, -2387, -502, +717, -908, +856, -362, -807, -179, -457, -950, -356, -981, -565, +70, -205, +52, -184, },
|
||||
{ +12424, +9043, +307, +7301, +1477, -1225, -2226, -1811, +2733, -2240, -3502, +1512, -1095, -2186, +52, -2767, -416, +476, -1043, +1163, -163, -967, -182, -310, -939, -309, -1033, -648, +172, -197, -66, -313, },
|
||||
{ +13850, +9410, -1577, +7710, +1903, -2007, -1727, -1126, +3784, -2445, -5373, +1368, +274, -2547, -214, -2859, -931, +830, -1404, +1283, +192, -1142, -131, -304, -939, -130, -968, -870, +367, -211, -179, -446, },
|
||||
{ +15689, +10650, -4191, +6439, +2855, -1819, -1216, -1122, +4356, -1735, -7082, +796, +1337, -2613, -354, -3165, -1020, +960, -1511, +1143, +338, -957, -254, -411, -1003, +93, -787, -961, +276, -120, -242, -590, },
|
||||
{ +17568, +11974, -6732, +4560, +3824, -1109, -754, -1349, +4606, -843, -8100, -287, +1911, -2056, -658, -3260, -1157, +899, -1412, +975, +487, -915, -443, -511, -909, +161, -686, -992, +148, +70, -223, -830, },
|
||||
{ +19484, +13084, -9012, +2638, +4382, +24, -354, -1619, +4661, +92, -8630, -1472, +1971, -1185, -834, -3443, -1007, +624, -1325, +1001, +434, -935, -492, -574, -862, +109, -756, -815, +84, +193, -327, -1033, },
|
||||
{ +21524, +13678, -10956, +1222, +4385, +1297, -139, -2069, +5017, +1156, -9307, -2582, +2096, -601, -528, -3661, -796, +152, -1389, +1321, +257, -1041, -461, -535, -1092, +104, -751, -637, +26, +127, -265, -1109, },
|
||||
{ +23605, +13718, -12574, +568, +3860, +2238, -103, -2760, +6167, +2019, -10424, -3226, +2099, -412, +299, -3787, -992, -294, -1323, +1597, -108, -1067, -318, -600, -1468, +200, -723, -642, -34, +135, -184, -1205, },
|
||||
{ +25071, +14148, -13932, +471, +3008, +2768, -76, -3611, +7895, +2700, -11593, -3451, +1646, -185, +1383, -3748, -1496, -431, -1263, +1534, -312, -957, -121, -752, -1802, +171, -636, -694, -65, +159, -201, -1248, },
|
||||
{ +26816, +13743, -14786, +862, +1921, +2946, -342, -4385, +10015, +2577, -12171, -3354, +671, +309, +2238, -3598, -1940, -465, -1278, +1149, -315, -796, +17, -1000, -2051, +76, -546, -729, -117, +138, -282, -1090, },
|
||||
{ +27951, +14104, -15590, +1273, +860, +2927, -633, -4986, +11529, +2744, -11887, -3827, +2, +498, +2997, -3184, -2478, -171, -1514, +556, -142, -469, -72, -1148, -2207, -3, -362, -847, -143, +170, -197, -1176, },
|
||||
{ +28803, +14580, -16123, +1472, -187, +3073, -1250, -5325, +12182, +3270, -11190, -4406, -641, +391, +3646, -2789, -2835, +205, -1933, -111, +62, -112, -330, -1167, -2390, +48, -212, -1112, -86, +341, -303, -1353, },
|
||||
{ +30323, +13889, -15910, +1407, -1171, +3553, -2383, -5267, +12463, +3540, -10099, -4882, -1197, +116, +4331, -2625, -2785, +375, -2274, -723, +301, +72, -494, -1132, -2662, +359, -286, -1255, +88, +360, -528, -1375, },
|
||||
{ +30968, +14210, -15671, +1048, -1781, +3667, -3019, -5566, +12074, +4371, -8903, -5378, -1555, -380, +4891, -2272, -2787, +479, -2516, -1197, +410, +143, -580, -995, -2832, +409, -254, -1218, +75, +188, -590, -1406, },
|
||||
{ +30975, +15165, -15312, +546, -2218, +3398, -2954, -6444, +11217, +5687, -7852, -5572, -1909, -880, +5215, -1819, -2692, +359, -2645, -1443, +355, +103, -656, -709, -2922, +164, -25, -1239, -84, +72, -690, -1273, },
|
||||
{ +30424, +16368, -14573, +92, -2674, +2748, -2205, -7930, +10058, +7372, -7052, -5344, -2312, -1403, +5238, -1260, -2430, -1, -2723, -1529, +312, -177, -560, -464, -2980, -55, -69, -1190, -231, -99, -594, -932, },
|
||||
{ +29455, +17526, -13363, -127, -3235, +1964, -1140, -9860, +8781, +9245, -6389, -4845, -2498, -2034, +4995, -530, -2084, -491, -2825, -1345, +221, -559, -398, -274, -2983, -380, -142, -1168, -371, +10, -384, -724, },
|
||||
{ +28118, +18400, -11563, -157, -4012, +1308, -199, -11792, +7222, +10997, -5639, -4233, -2301, -2934, +4504, +364, -1845, -1024, -2737, -1299, +240, -966, -422, -53, -3194, -483, -435, -1176, -132, +127, -258, -452, },
|
||||
{ +26552, +19152, -9368, -160, -4867, +986, +473, -13162, +5114, +12399, -4354, -3860, -1375, -4191, +3967, +1296, -1787, -1124, -2703, -1337, +286, -1074, -1007, +244, -3127, -928, -436, -993, +94, +254, +31, -338, },
|
||||
{ +24559, +20217, -7367, -220, -5053, +163, +912, -13440, +2241, +12957, -1945, -4140, -3, -5230, +2779, +2633, -1975, -895, -2652, -1685, +399, -1133, -1787, +451, -3100, -1099, -141, -1148, +476, +550, +59, -556, },
|
||||
{ +23258, +20668, -6394, +41, -4001, -1343, +259, -12280, -447, +12376, +814, -4410, +990, -5553, +1277, +3778, -1842, -823, -2296, -2428, +66, -675, -2623, +69, -2488, -1174, +108, -1212, +763, +685, -33, -620, },
|
||||
{ +23142, +19464, -5694, +1043, -3149, -2286, -1288, -10731, -2354, +11827, +2425, -4171, +1875, -6154, +693, +4142, -1325, -613, -2234, -3206, -20, -772, -3178, +251, -2328, -913, +289, -1277, +940, +736, -150, -671, },
|
||||
{ +22552, +18662, -5393, +2289, -1879, -3683, -2436, -8894, -4473, +11069, +4563, -5019, +3166, -6153, -527, +5142, -1047, -592, -2109, -3335, -857, -775, -3407, +489, -1798, -1280, +535, -1129, +1014, +574, -236, -416, },
|
||||
{ +22401, +17758, -5842, +3364, -812, -4784, -3176, -6717, -5712, +8976, +6974, -6176, +3397, -4371, -2591, +6388, -734, -1317, -1243, -3549, -1586, -824, -3469, +452, -886, -1990, +679, -751, +619, +748, -301, -324, },
|
||||
{ +22309, +16537, -6181, +4660, -360, -5405, -3571, -5406, -6202, +6988, +8656, -6084, +2058, -2265, -3682, +6212, +772, -2272, -843, -3239, -2023, -881, -3727, +370, -424, -2103, +394, -539, +433, +863, -135, -441, },
|
||||
{ +21791, +15541, -6192, +5616, -505, -5546, -3072, -4658, -6616, +5207, +9841, -5865, +904, -1208, -3643, +5823, +1664, -2499, -798, -2591, -2431, -478, -3918, -635, -47, -1868, +308, -596, +226, +890, +22, -361, },
|
||||
{ +21324, +14642, -6583, +6799, -1051, -6105, -1417, -3904, -7362, +3483, +10991, -5815, -496, +549, -4041, +4979, +2787, -2309, -896, -2206, -2499, +60, -3674, -2156, -266, -1362, +446, -246, -422, +573, +555, -41, },
|
||||
{ +20752, +13927, -7556, +7887, -383, -7140, -472, -2508, -7640, +1050, +11820, -4742, -2329, +2225, -4014, +3077, +4488, -1969, -1094, -1484, -3038, +789, -3176, -3637, -596, -1084, +354, +499, -803, -174, +1114, +269, },
|
||||
{ +19965, +13135, -8141, +8473, +760, -7513, -787, -971, -6960, -1942, +12233, -3129, -3764, +3112, -3442, +1359, +4791, -618, -1183, -1102, -2895, +601, -2684, -3990, -954, -1401, +153, +1055, -625, -482, +888, +405, },
|
||||
{ +19143, +11685, -8033, +9384, +754, -7209, -418, -807, -6484, -2771, +11238, -2375, -3772, +3580, -3133, +167, +4612, -12, -608, -927, -2580, +196, -2728, -3474, -1079, -1676, -444, +1107, -287, -167, +508, -54, },
|
||||
{ +17857, +10290, -7109, +9878, +454, -6436, -46, -1046, -5905, -3006, +10009, -1912, -3542, +3960, -2520, -1113, +4554, +356, -838, +40, -2468, -172, -2487, -3471, -559, -1557, -1179, +703, -47, +127, +577, -318, },
|
||||
{ +16296, +9009, -5714, +9900, +226, -5547, +70, -922, -5384, -3238, +8823, -1282, -3406, +3868, -1537, -1923, +3996, +793, -1145, +249, -1720, -492, -2325, -3083, -467, -1071, -1564, +201, -55, +203, +765, -336, },
|
||||
{ +15841, +6997, -6669, +11869, +1968, -7696, -859, +385, -4403, -1539, +6206, -3126, -2033, +3213, -1498, -707, +2441, -260, -30, +640, -527, +1221, -934, -1381, +294, -1105, -1226, -270, -1487, -1152, -350, -611, },
|
||||
{ +13568, +6667, -4985, +10337, +1992, -6022, -629, +40, -3621, -1574, +5221, -2329, -1990, +2774, -1185, -903, +2184, +6, -131, +493, -301, +1057, -963, -1225, +288, -1053, -961, -56, -1078, -893, -506, -522, },
|
||||
{ +11497, +6501, -3490, +8587, +2327, -4433, -803, -59, -2956, -1671, +4442, -1603, -1990, +2273, -678, -964, +1803, +277, -100, +341, -258, +879, -733, -1205, +106, -817, -829, +182, -726, -774, -439, -507, },
|
||||
{ +9657, +6295, -2357, +7055, +2839, -3195, -922, +87, -2426, -1717, +3756, -1022, -1971, +1799, -285, -852, +1503, +322, +17, +329, -329, +745, -562, -1092, +21, -498, -600, +37, -450, -530, -354, -499, },
|
||||
{ +8080, +5958, -1527, +5929, +3206, -2314, -807, +279, -2098, -1713, +3210, -663, -1953, +1446, +14, -682, +1140, +403, +168, +150, -283, +600, -477, -850, +175, -366, -595, +17, -334, -374, -332, -390, },
|
||||
{ +6742, +5563, -934, +5119, +3472, -1654, -579, +468, -1830, -1693, +2733, -440, -1854, +1199, +270, -615, +927, +597, +39, +58, -146, +541, -282, -642, +203, -260, -523, -66, -324, -342, -189, -288, },
|
||||
{ +5645, +5082, -470, +4564, +3569, -1149, -280, +632, -1648, -1657, +2279, -195, -1747, +997, +500, -550, +854, +535, -56, +205, +54, +502, -244, -453, +284, -250, -567, -159, -320, -231, -40, -147, },
|
||||
{ +4739, +4548, -15, +4161, +3424, -612, +92, +576, -1502, -1483, +1923, -133, -1573, +1049, +513, -481, +827, +418, +204, +407, -41, +456, -23, -319, +105, -295, -566, -165, -125, -78, -45, -303, },
|
||||
{ +4000, +4116, +315, +3739, +3361, -78, +141, +471, -1088, -1423, +1428, +104, -1192, +778, +494, -118, +776, +531, +352, +301, +46, +496, -16, -403, -75, -250, -372, +7, -75, -290, -272, -334, },
|
||||
{ +3315, +3731, +744, +3252, +3208, +505, +104, +326, -724, -1258, +1022, +253, -920, +621, +766, +320, +638, +429, +479, +312, +62, +342, -149, -425, +78, -14, -324, -163, -364, -342, -177, -191, },
|
||||
{ +2721, +3507, +1076, +2691, +3219, +926, -155, +334, -235, -1266, +533, +565, -478, +790, +1052, +241, +481, +577, +434, +197, -68, +166, +67, -116, +62, -279, -535, -276, -242, -195, -120, -171, },
|
||||
{ +2164, +3272, +1472, +2240, +2998, +1160, -8, +380, -347, -1061, +577, +981, +39, +670, +997, +273, +443, +502, +183, +101, +156, +367, +108, -320, -359, -368, -337, -186, -156, -185, -219, -252, },
|
||||
{ +1934, +3190, +1503, +1841, +2991, +1228, -234, +344, -22, -479, +868, +985, +160, +811, +840, +155, +222, +329, +446, +340, +153, +57, -306, -454, -236, -196, -253, -198, -267, -270, -212, -218, },
|
||||
{ +1850, +2863, +1570, +2109, +2264, +770, +377, +952, +272, -269, +977, +887, +259, +859, +502, -89, +465, +710, +360, -49, -274, -104, -140, -299, -180, -223, -354, -327, -223, -222, -195, -175, },
|
||||
{ +1880, +2606, +1259, +1967, +2141, +1272, +1090, +983, +171, -143, +1137, +1038, +78, +386, +659, +433, +428, +299, -214, -286, +0, +94, -163, -340, -274, -342, -349, -267, -185, -155, -161, -219, },
|
||||
{ +1397, +2225, +1498, +2218, +2597, +1375, +970, +1179, +446, +55, +983, +782, +92, +766, +779, +21, -117, -30, -43, +45, +38, -51, -292, -398, -291, -331, -321, -187, -130, -199, -179, -163, },
|
||||
{ +1635, +3062, +2249, +1880, +1916, +1343, +1114, +965, +534, +333, +634, +1026, +601, -88, -96, -36, +19, +140, +79, -54, -187, -223, -327, -363, -331, -232, -228, -189, -124, -136, -165, -244, },
|
||||
{ +1621, +2982, +2331, +1859, +1672, +1505, +1317, +1168, +916, +338, +486, +1012, +283, -527, -167, +103, +151, +173, -30, -202, -207, -185, -337, -340, -290, -264, -241, -183, -115, -121, -178, -226, },
|
||||
{ +1602, +2643, +1775, +1564, +1563, +1546, +1646, +1829, +1322, +708, +618, +118, +151, +107, -304, -55, +284, +102, -151, -125, -138, -202, -309, -317, -234, -259, -302, -184, -120, -109, -170, -206, },
|
||||
{ +1689, +2725, +1687, +1342, +1018, +1221, +1839, +1792, +1379, +895, +468, +478, +768, +67, -215, -9, -169, +55, +46, -227, -303, -177, -209, -261, -200, -191, -162, -205, -213, -154, -174, -239, },
|
||||
{ +1884, +2685, +1171, +1190, +1175, +880, +977, +1283, +1525, +1401, +1097, +684, +686, +975, +274, -642, -241, -46, -48, +8, -211, -255, -203, -172, -169, -156, -217, -131, -186, -249, -160, -234, },
|
||||
{ +1711, +2523, +1308, +1090, +1055, +1174, +1022, +299, +477, +1301, +1459, +1119, +863, +804, +1042, +601, -340, -501, -204, -29, -21, -94, -220, -196, -156, -184, -116, -170, -201, -183, -255, -271, },
|
||||
{ +1817, +2536, +946, +809, +944, +1085, +1157, +217, -175, +767, +1098, +779, +1147, +1424, +1313, +1026, +631, +198, -302, -492, -270, -64, -131, -189, -253, -238, -160, -88, -70, -213, -184, -209, },
|
||||
{ +2191, +2839, +854, +984, +1168, +1002, +768, -219, -19, +1063, +561, +60, +636, +861, +1348, +1294, +885, +891, +475, -73, -432, -506, -288, -67, -118, -220, -240, -103, -74, -138, -148, -245, },
|
||||
{ +2451, +2833, +673, +1459, +1780, +1173, +305, -947, -35, +1449, +504, -211, +108, +410, +948, +640, +824, +1192, +884, +675, +272, -177, -565, -472, -224, -108, -84, -125, -187, -164, -89, -188, },
|
||||
{ +3086, +3702, +648, +1075, +1515, +1169, +441, -1098, -143, +1309, +271, -221, +106, +208, +526, +378, +428, +792, +941, +752, +488, +344, +62, -247, -504, -376, -181, +22, -47, -250, -217, -168, },
|
||||
{ +3569, +4097, +532, +1316, +1840, +1093, -29, -1514, +254, +1604, -129, -469, -58, +189, +685, +94, +33, +566, +399, +418, +683, +566, +398, +298, -51, -324, -429, -122, -77, -175, -112, -284, },
|
||||
{ +4125, +4461, +378, +1609, +2191, +994, -587, -1865, +674, +1689, -401, -500, -250, +88, +680, -19, +36, +317, +78, +227, +154, +381, +584, +347, +348, +259, -144, -157, -249, -295, -182, -189, },
|
||||
{ +4867, +5214, +256, +1466, +2150, +866, -894, -1915, +1051, +1495, -779, -414, -262, +45, +633, -205, +3, +311, -89, +0, -11, +125, +199, +237, +418, +383, +270, +312, -107, -370, -261, -364, },
|
||||
{ +5316, +5480, +203, +2110, +2686, +411, -1842, -1931, +1799, +1361, -1123, -406, -380, +95, +608, -401, +32, +304, -272, -6, -184, -72, +198, -153, +69, +403, +277, +536, +348, -42, -176, -397, },
|
||||
{ +5892, +6233, +160, +2100, +2860, +142, -2433, -1800, +2326, +963, -1427, -273, -439, +158, +610, -591, -43, +281, -390, -16, -246, -165, +68, -360, -9, +156, +82, +484, +326, +166, +189, -170, },
|
||||
{ +6296, +6975, +282, +2083, +3088, -133, -3093, -1657, +2878, +590, -1820, -143, -472, +190, +638, -719, -106, +233, -510, -92, -328, -237, +113, -522, -223, +109, -26, +254, +179, +32, +200, +156, },
|
||||
{ +6592, +7845, +648, +1757, +3189, -161, -3619, -1575, +3255, +370, -2151, -143, -426, +168, +727, -779, -195, +244, -572, -180, -428, -336, +66, -505, -282, -26, -117, +217, +77, -98, +16, -149, },
|
||||
{ +6774, +8708, +1082, +1524, +3240, -264, -3922, -1540, +3434, +295, -2349, -304, -372, +149, +756, -675, -383, +223, -505, -236, -520, -494, -9, -552, -315, -62, -148, +121, +173, -299, -365, -62, },
|
||||
{ +6908, +9422, +1625, +1424, +3190, -389, -4004, -1611, +3471, +335, -2543, -391, -396, +68, +811, -604, -534, +182, -441, -272, -536, -643, -94, -634, -428, -131, -91, +267, -223, -392, -234, -275, },
|
||||
{ +7138, +10189, +2126, +1133, +2968, -382, -3722, -1762, +3018, +580, -2559, -606, -255, -134, +669, -325, -681, -12, -293, -317, -621, -648, -141, -715, -656, -53, -111, -126, -38, -347, -470, -236, },
|
||||
{ +7313, +10455, +2943, +1070, +2390, +272, -3497, -2310, +2731, +755, -2380, -670, -474, -246, +602, -255, -558, -320, -482, -158, -477, -799, -319, -604, -503, -410, -501, +108, -160, -398, -359, -347, },
|
||||
{ +7826, +10161, +3173, +1967, +2130, +423, -3555, -2463, +2451, +634, -1952, -839, -706, -181, +512, -312, -450, -587, -636, +17, -569, -882, -377, -371, -655, -672, -473, -170, -109, -345, -438, -377, },
|
||||
{ +8467, +9477, +3103, +3430, +2024, +440, -3655, -2401, +2236, -26, -1277, -712, -1114, -26, +375, -495, -221, -726, -826, +29, -851, -620, -323, -809, -370, -647, -794, -102, -163, -412, -424, -337, },
|
||||
{ +9286, +8516, +2179, +5700, +2660, -483, -3558, -1721, +1619, -1074, -311, -359, -1649, +39, +247, -528, -41, -857, -1059, -36, -827, -638, -733, -625, -208, -781, -693, -299, -176, -287, -276, -526, },
|
||||
{ +10487, +8297, +724, +7001, +3276, -1546, -2976, -375, +714, -2874, +551, +663, -2103, -61, +34, -604, +320, -1061, -1370, +301, -1334, -957, -278, -1039, -89, -630, -732, -236, -218, -112, -316, -769, },
|
||||
{ +11694, +8483, -1085, +7688, +4377, -2639, -2748, +1388, +290, -5170, +695, +2234, -2233, -376, -167, -879, +703, -1107, -1401, -21, -1755, -792, -436, -1099, +19, -701, -676, +56, -177, -420, -299, -499, },
|
||||
{ +12813, +8986, -2960, +7720, +5822, -3441, -2888, +3186, +503, -7344, -143, +3835, -1619, -861, -365, -1277, +915, -643, -1945, -184, -1723, -1371, -165, -1036, -149, -608, -623, +361, -273, -675, -81, -360, },
|
||||
{ +14282, +9854, -5144, +6666, +7543, -3247, -3573, +4303, +1159, -8673, -1552, +4734, -435, -1066, -737, -1669, +1323, -711, -2400, +154, -2059, -1735, -117, -896, -79, -641, -571, +431, -255, -662, -37, -376, },
|
||||
{ +15850, +10514, -7059, +5564, +8654, -2773, -4154, +5402, +1737, -9668, -3061, +5015, +1018, -866, -1440, -1886, +1527, -978, -2388, +41, -2052, -1988, -323, -629, -13, -650, -699, +444, -203, -546, -140, -354, },
|
||||
{ +17808, +11057, -9134, +4383, +9339, -1778, -4546, +6062, +2149, -10167, -4257, +4712, +2487, -456, -1988, -2239, +1739, -1173, -2546, +134, -2148, -2055, -395, -540, +91, -587, -935, +488, -19, -574, -174, -369, },
|
||||
{ +20383, +11132, -11423, +3225, +9645, -449, -4728, +6097, +2297, -10259, -4895, +4011, +3369, +308, -2432, -2790, +2050, -1452, -2764, +266, -2368, -1811, -610, -479, +220, -714, -1063, +583, +89, -687, -231, -413, },
|
||||
{ +22418, +11798, -13840, +2373, +9532, +812, -4649, +5776, +2876, -10183, -5672, +3197, +3848, +1355, -2692, -3324, +2269, -1738, -2840, +474, -2481, -1785, -640, -534, +229, -841, -1060, +727, -62, -819, -169, -447, },
|
||||
{ +24894, +11396, -15645, +2370, +8933, +1292, -4281, +5629, +3954, -10488, -6364, +2682, +3969, +2394, -2882, -3405, +2175, -2174, -2424, +697, -2794, -1803, -440, -660, +159, -906, -842, +819, -413, -760, -126, -429, },
|
||||
{ +26219, +12293, -17420, +2685, +7926, +1207, -3924, +5306, +5940, -10486, -7929, +2418, +4103, +2810, -2438, -3395, +1620, -2289, -1991, +723, -2963, -1849, -215, -902, +78, -722, -742, +870, -694, -718, -23, -517, },
|
||||
{ +28416, +11175, -18030, +3639, +6517, +524, -3997, +5626, +7964, -10885, -9328, +2435, +4025, +2769, -1736, -3204, +870, -2252, -1569, +634, -3242, -1541, -240, -1235, +91, -529, -623, +850, -941, -678, +99, -756, },
|
||||
{ +29176, +11689, -18551, +4399, +5252, -160, -4585, +5761, +10465, -11051, -10563, +2051, +3915, +2524, -384, -3448, +237, -1642, -1718, +678, -3401, -1225, -371, -1551, +5, -226, -521, +663, -896, -768, +144, -1146, },
|
||||
{ +29021, +13077, -18841, +4810, +4427, -776, -5593, +5564, +12643, -10486, -11302, +1156, +3758, +2201, +1006, -3601, -405, -695, -2158, +611, -3242, -1002, -587, -1809, -96, +32, -433, +355, -533, -1081, -66, -1282, },
|
||||
{ +30639, +12013, -18276, +5087, +3533, -892, -7519, +6359, +13438, -9871, -11200, +130, +3528, +2142, +1859, -3611, -530, -415, -2173, +244, -2952, -752, -1000, -1830, -214, +226, -496, +424, -528, -1428, -247, -1261, },
|
||||
{ +31129, +12133, -17589, +4753, +3258, -1123, -8951, +6078, +13955, -8365, -10965, -1246, +3253, +2362, +2106, -3221, -543, -421, -2041, -9, -2564, -798, -1213, -1842, -312, +214, -362, +364, -897, -1374, -502, -1281, },
|
||||
{ +30719, +13121, -16780, +4200, +3198, -1555, -9649, +4676, +14307, -6216, -10726, -2675, +2883, +2776, +1878, -2607, -273, -769, -1833, +57, -2369, -947, -1301, -1967, -305, +248, -529, +122, -1019, -1330, -719, -996, },
|
||||
{ +29697, +14733, -15818, +3462, +3061, -1780, -9577, +2144, +14501, -3727, -10382, -3699, +2209, +3218, +1410, -1818, +264, -1375, -1409, +257, -2249, -1183, -1411, -1971, -160, -60, -974, +340, -1284, -1182, -516, -1030, },
|
||||
{ +28002, +16846, -14478, +2412, +2676, -1519, -8873, -1372, +14440, -1063, -9859, -4131, +1096, +3328, +1195, -1203, +1073, -1927, -1076, +655, -2259, -1480, -1460, -1775, -599, -341, -1181, +468, -1245, -932, -470, -1164, },
|
||||
{ +25869, +18455, -12266, +1637, +1978, -1262, -8280, -4934, +14128, +1672, -9443, -4041, -152, +2529, +1995, -1259, +1882, -1726, -1545, +1491, -2530, -1653, -1433, -2120, -1079, -408, -1374, +717, -856, -1223, -178, -1188, },
|
||||
{ +24864, +17912, -9358, +1641, +576, -357, -8615, -7257, +13351, +3934, -9073, -3482, -838, +841, +3250, -1322, +2217, -796, -2111, +1754, -2153, -2158, -1467, -2434, -1624, -253, -1302, +874, -576, -1341, -96, -1263, },
|
||||
{ +23420, +17860, -6501, +1165, -717, +588, -8140, -9637, +11644, +6449, -8639, -3126, -461, -1536, +4057, -378, +1614, +434, -2303, +1248, -1439, -2499, -1782, -2484, -2002, -134, -998, +651, -243, -1476, -235, -998, },
|
||||
{ +22006, +18635, -5893, +1609, -295, -1121, -6611, -10352, +8438, +8556, -7366, -3845, +1086, -3469, +3525, +1549, +597, +1088, -1819, +227, -740, -2416, -2356, -1773, -2500, -256, -642, +230, -150, -1358, -351, -903, },
|
||||
{ +22224, +17778, -6386, +3076, +518, -3282, -6409, -9035, +5543, +9128, -5573, -4587, +2339, -4194, +2628, +2481, +473, +849, -1362, -75, -999, -1551, -2497, -1277, -2514, -1057, -402, -20, +22, -1408, -555, -809, },
|
||||
{ +22103, +17015, -6884, +4461, +1233, -5006, -6070, -7906, +2832, +9189, -3231, -5657, +3433, -4282, +1463, +3242, -717, +1493, -1177, -711, -254, -881, -2411, -866, -2491, -1884, -465, -353, +374, -1387, -1115, -592, },
|
||||
{ +22560, +15777, -7251, +5550, +797, -5686, -5009, -7186, +427, +9113, -1133, -6601, +3854, -2859, -422, +3276, -587, +291, -113, -529, +173, -114, -2517, -809, -1886, -2312, -1324, -304, +246, -1137, -1252, -701, },
|
||||
{ +22731, +15350, -9222, +6868, +2173, -7663, -4747, -4975, -1211, +7055, +1768, -6971, +2930, -1113, -1589, +2833, -506, -636, +510, +745, -151, +413, -2257, -1147, -1254, -2415, -1827, -858, -136, -928, -699, -812, },
|
||||
{ +23304, +14017, -11279, +9072, +3099, -9814, -4178, -2550, -2580, +4834, +3988, -6788, +1843, +177, -2381, +2443, -295, -988, +882, +1070, +364, +564, -2129, -1074, -1171, -2223, -1782, -1409, -1115, -600, -62, -717, },
|
||||
{ +23618, +12255, -12665, +11454, +3209, -11235, -3282, -912, -3248, +2468, +5358, -5978, +1078, +518, -3093, +2872, +91, -1576, +1462, +574, +484, +1620, -2554, -835, -966, -2532, -1116, -1629, -2136, -622, +243, -448, },
|
||||
{ +23507, +10176, -12976, +13613, +2604, -11794, -2177, +221, -4379, +941, +6866, -5844, +60, +1571, -3421, +2227, +1420, -1610, +744, +868, +160, +1913, -1757, -1375, -527, -2462, -1299, -900, -2681, -1245, +284, -163, },
|
||||
{ +22437, +8395, -11784, +14554, +1462, -10835, -1161, -107, -4923, +595, +7185, -5944, -419, +2664, -3581, +1327, +2496, -1354, +156, +805, -57, +1910, -1336, -1388, -199, -2269, -1525, -558, -2342, -1654, -242, -88, },
|
||||
{ +20519, +7379, -9918, +14135, +1130, -9407, -1215, -80, -4674, -389, +7077, -5070, -1127, +3004, -2779, +575, +2442, -923, +375, +352, -401, +2124, -1583, -1128, +325, -2275, -1384, -459, -2045, -1486, -593, -444, },
|
||||
{ +18163, +7171, -8342, +13120, +1703, -8708, -1203, +485, -4645, -1323, +6947, -3981, -1909, +3255, -1914, -231, +2372, -512, +246, +503, -605, +1638, -1102, -1353, +431, -1611, -1452, -256, -1866, -1326, -460, -644, },
|
||||
{ +14734, +5366, -4938, +13261, +853, -8371, +138, +318, -3336, +134, +2204, -3293, +1697, +2006, -2237, -277, +1106, -520, -774, +253, -99, +68, -412, +780, +809, +219, +346, -370, -906, -336, -779, -1298, },
|
||||
{ +12591, +5345, -3597, +11397, +1333, -6737, -45, +46, -2876, +145, +2069, -2657, +1229, +1805, -1739, -516, +791, -312, -657, +44, -12, +385, -291, +437, +830, +261, +90, -313, -700, -424, -611, -816, },
|
||||
{ +10805, +5339, -2604, +9725, +1921, -5525, -310, +43, -2429, +58, +1959, -2032, +690, +1674, -1216, -766, +650, -159, -575, +29, +105, +445, -262, +423, +590, +102, +242, -169, -667, -426, -434, -541, },
|
||||
{ +9340, +5201, -1982, +8478, +2304, -4592, -320, +86, -2009, +7, +1880, -1679, +320, +1616, -995, -817, +572, -17, -496, +83, +280, +281, -168, +371, +343, +122, +380, -5, -602, -322, -317, -483, },
|
||||
{ +8188, +4843, -1495, +7540, +2395, -3667, -312, +176, -1589, -77, +1770, -1446, +114, +1442, -804, -782, +564, +82, -423, +254, +211, +181, -62, +208, +366, +208, +389, +140, -452, -279, -247, -426, },
|
||||
{ +7139, +4447, -1005, +6621, +2495, -2780, -305, +313, -1320, -50, +1606, -1337, +16, +1312, -654, -711, +624, +129, -237, +218, +117, +290, -83, +217, +421, +287, +411, +153, -317, -192, -175, -342, },
|
||||
{ +6157, +4147, -661, +5795, +2719, -2091, -278, +447, -1015, -108, +1345, -1106, -12, +1133, -544, -455, +616, +156, -87, +162, +222, +352, -56, +280, +494, +294, +391, +184, -187, +6, -183, -528, },
|
||||
{ +5279, +3795, -302, +5095, +2792, -1423, -200, +524, -834, -167, +1181, -897, -46, +946, -299, -307, +520, +329, -7, +248, +337, +305, +68, +311, +476, +300, +386, +295, -101, -189, -428, -616, },
|
||||
{ +4525, +3459, -28, +4546, +2834, -885, -89, +549, -694, -192, +1043, -644, -76, +759, -74, -153, +593, +505, +119, +343, +347, +334, +158, +340, +453, +359, +386, +40, -308, -328, -435, -568, },
|
||||
{ +3930, +3246, +221, +4012, +2721, -518, +143, +558, -699, -125, +1073, -572, -271, +859, +240, +26, +775, +548, +199, +453, +346, +378, +240, +360, +327, +89, +121, -123, -319, -354, -529, -642, },
|
||||
{ +3660, +3270, +329, +3382, +2495, -259, +336, +625, -667, -168, +978, -390, -146, +1158, +544, +64, +837, +706, +404, +422, +317, +415, +105, -19, +35, +69, +43, -172, -504, -539, -442, -551, },
|
||||
{ +3142, +2802, +557, +3432, +2389, -71, +540, +456, -686, +42, +1063, -160, +119, +1196, +649, +468, +1015, +684, +339, +353, +244, +13, -220, -126, +48, +81, -160, -332, -500, -382, -372, -555, },
|
||||
{ +2901, +2760, +686, +3156, +2256, -27, +369, +564, -227, +172, +1095, +54, +380, +1501, +986, +535, +850, +558, +60, -34, -144, -62, -229, -185, +27, -136, -229, -282, -406, -348, -382, -501, },
|
||||
{ +2508, +2594, +1005, +2823, +1909, +401, +624, +677, +80, +149, +1195, +622, +765, +1450, +899, +441, +488, -3, -301, -63, -155, -197, -378, -249, +15, -114, -216, -325, -386, -326, -435, -465, },
|
||||
{ +2206, +2342, +933, +2683, +2333, +903, +669, +761, +492, +499, +1258, +984, +954, +1127, +508, -78, +61, +24, -139, -253, -442, -276, -171, -30, -76, -244, -225, -191, -331, -440, -399, -321, },
|
||||
{ +1974, +2815, +1553, +2323, +2233, +1046, +992, +1282, +753, +707, +1323, +599, +390, +690, +158, -204, -61, -149, -350, -307, -284, -187, -98, -47, -143, -282, -239, -272, -323, -315, -319, -272, },
|
||||
{ +1654, +2776, +1836, +2401, +3022, +1959, +967, +1094, +948, +444, +652, +267, +140, +546, +166, -270, -194, -277, -387, -222, -184, -102, -101, -213, -211, -214, -174, -207, -336, -339, -249, -198, },
|
||||
{ +1869, +3354, +2494, +2832, +2716, +1654, +1450, +1127, +173, -258, +546, +432, -146, +288, +167, -560, -266, -9, -321, -261, -193, -155, -101, -190, -238, -205, -240, -223, -233, -279, -241, -223, },
|
||||
{ +2251, +4370, +3095, +2273, +2143, +1471, +1134, +828, +318, +171, +218, -108, -7, +202, -108, -329, -135, -186, -250, -123, -231, -173, -170, -206, -184, -240, -217, -223, -224, -243, -163, -183, },
|
||||
{ +2659, +4415, +2723, +1899, +1660, +1369, +1422, +927, +331, +708, +366, -215, -222, -87, +228, -79, -456, -205, +38, -181, -201, -158, -281, -171, -233, -218, -175, -209, -188, -206, -158, -146, },
|
||||
{ +2110, +3925, +2617, +1488, +1521, +1321, +1000, +1375, +1006, +577, +1002, +281, -242, +99, -57, -249, -82, -20, -257, -120, -41, -272, -333, -251, -182, -210, -176, -126, -157, -179, -176, -194, },
|
||||
{ +1815, +3251, +2187, +1550, +1381, +941, +1057, +773, +515, +1445, +1370, +807, +715, +171, +116, +49, -344, -256, +25, -69, -365, -300, -115, -199, -312, -112, -74, -117, -199, -216, -192, -267, },
|
||||
{ +1453, +2017, +1172, +1714, +1993, +1501, +661, -199, +441, +1198, +925, +1207, +1374, +1313, +1201, +202, -18, +29, -479, -298, -15, -198, -383, -148, -84, -172, -178, -112, -77, -224, -241, -216, },
|
||||
{ +1882, +2639, +981, +885, +1287, +1149, +657, -264, +342, +1130, +630, +742, +1292, +1636, +1472, +886, +568, +182, -94, -158, -412, -373, +6, -144, -402, -52, +23, -169, -270, -217, -171, -242, },
|
||||
{ +2138, +2960, +1101, +1064, +1446, +1136, +291, -438, +715, +1105, +64, +265, +594, +767, +1266, +1060, +858, +969, +438, -42, -101, -297, -344, -218, -121, -114, -231, -67, -73, -307, -272, -133, },
|
||||
{ +2460, +3291, +1078, +1222, +1735, +1211, -80, -710, +954, +1086, -141, +205, +328, +431, +693, +253, +688, +1146, +655, +645, +331, -174, -61, -188, -361, -99, -16, -187, -277, -187, -156, -227, },
|
||||
{ +2893, +3811, +1105, +1203, +1818, +1171, -395, -714, +1243, +818, -421, +245, +254, +371, +579, -124, +129, +613, +507, +652, +517, +400, +268, -71, -95, -9, -228, -164, -144, -359, -299, -148, },
|
||||
{ +3308, +4221, +1114, +1356, +2071, +1044, -891, -658, +1576, +583, -594, +223, +142, +308, +585, -177, -43, +346, +34, +199, +317, +405, +505, +368, +236, +145, +52, -93, -322, -363, -224, -252, },
|
||||
{ +3900, +4867, +998, +1260, +2220, +839, -1162, -422, +1621, +246, -664, +254, +85, +242, +512, -240, -75, +270, -146, -7, -102, +10, +325, +287, +509, +621, +248, +30, -87, -358, -365, -226, },
|
||||
{ +4379, +5375, +1025, +1329, +2414, +620, -1641, -92, +1872, -196, -752, +327, -63, +215, +556, -301, -147, +211, -180, -88, -262, -176, -67, -67, +404, +602, +521, +379, +47, -155, -101, -270, },
|
||||
{ +4917, +5923, +933, +1451, +2663, +215, -2061, +412, +1970, -661, -792, +379, -89, +99, +514, -309, -198, +208, -222, -155, -360, -296, -176, -258, +109, +365, +340, +336, +360, +108, +10, -101, },
|
||||
{ +5325, +6387, +1044, +1617, +2857, -114, -2569, +894, +2226, -1191, -880, +471, -93, +10, +443, -288, -328, +216, -163, -257, -444, -325, -267, -361, +2, +132, +152, +154, +168, +115, +205, +89, },
|
||||
{ +5680, +6946, +1250, +1595, +2986, -178, -3103, +1203, +2516, -1694, -900, +579, -179, -49, +355, -219, -348, +53, -131, -293, -494, -294, -363, -474, -96, +59, +126, -24, -77, -107, +65, +185, },
|
||||
{ +6420, +7593, +839, +1808, +3146, -725, -3065, +1505, +2329, -1963, -819, +612, -243, -161, +340, -128, -458, -62, -108, -328, -544, -296, -472, -531, -38, -40, +40, -115, -239, -173, -70, -95, },
|
||||
{ +6890, +8125, +798, +2033, +3239, -1037, -3175, +1581, +2439, -2196, -937, +706, -276, -254, +344, -74, -579, -163, -59, -291, -673, -428, -436, -541, -74, -59, -108, -153, -248, -283, -175, -228, },
|
||||
{ +7112, +8504, +1011, +2420, +3353, -1361, -3255, +1549, +2556, -2243, -1200, +741, -167, -319, +199, +28, -584, -316, +19, -391, -797, -406, -435, -548, -248, -164, +60, -169, -402, -374, -175, -285, },
|
||||
{ +7285, +8820, +1447, +2597, +3239, -1169, -3229, +1126, +2635, -2125, -1498, +885, -99, -519, +165, +47, -347, -428, -288, -299, -829, -481, -398, -641, -383, -145, +82, -141, -416, -423, -333, -478, },
|
||||
{ +7520, +8869, +2021, +2814, +2876, -582, -3127, +494, +2536, -1844, -1576, +724, +34, -625, -7, +239, -155, -630, -485, -225, -859, -553, -623, -612, -241, -315, +42, -51, -367, -610, -501, -479, },
|
||||
{ +7879, +8478, +2398, +3646, +2683, -425, -3110, +367, +2156, -1723, -1287, +263, +38, -359, -259, +217, +107, -737, -613, -82, -1068, -775, -685, -547, -228, -402, +0, +62, -472, -776, -425, -752, },
|
||||
{ +8205, +7639, +2366, +5376, +3131, -1245, -3272, +1332, +1408, -2244, -393, -194, -320, +234, -603, -6, +519, -774, -631, -35, -1347, -848, -738, -677, -167, -469, +8, -127, -364, -733, -690, -706, },
|
||||
{ +9071, +7121, +1359, +7044, +3754, -2464, -2808, +2381, +60, -2776, +793, -372, -1036, +563, -545, -275, +838, -803, -718, +19, -1374, -976, -1066, -680, +3, -628, -230, -55, -414, -742, -545, -882, },
|
||||
{ +10148, +7203, -207, +7831, +4875, -3407, -2535, +3529, -1225, -3617, +2016, -129, -1945, +583, -98, -688, +1031, -623, -939, +116, -1314, -1178, -1259, -633, -72, -818, -379, -21, -446, -673, -502, -1013, },
|
||||
{ +11044, +7769, -1943, +7882, +6483, -3989, -2591, +4784, -2149, -4901, +3054, +635, -2734, +50, +522, -825, +921, -244, -1180, +190, -1284, -1180, -1357, -904, -121, -897, -516, -101, -271, -733, -400, -911, },
|
||||
{ +11990, +8411, -3500, +7279, +8002, -4096, -2789, +6028, -2844, -6395, +3677, +1736, -3160, -867, +846, -555, +671, +81, -1262, +34, -1076, -1221, -1513, -1089, -353, -964, -456, -261, -256, -672, -256, -770, },
|
||||
{ +13335, +8653, -4769, +6560, +8870, -3919, -2715, +7255, -3621, -7665, +3846, +2827, -3088, -1854, +732, -91, +613, +191, -1151, -200, -889, -1355, -1616, -1202, -620, -945, -484, -248, -378, -527, -120, -768, },
|
||||
{ +14852, +8670, -5913, +6073, +9001, -3654, -2195, +8358, -4463, -8616, +3590, +3721, -2575, -2699, +236, +272, +868, +133, -945, -394, -1059, -1238, -1780, -1341, -641, -1048, -561, -27, -440, -570, -19, -740, },
|
||||
{ +16451, +8693, -7064, +5832, +8573, -3576, -1333, +9514, -5301, -9295, +3094, +4207, -1784, -3244, -399, +318, +1449, +54, -659, -662, -1314, -1021, -2073, -1203, -803, -1212, -404, +103, -451, -722, -56, -638, },
|
||||
{ +18423, +8132, -8075, +6128, +7681, -4035, +138, +10573, -6216, -9800, +2747, +4350, -1192, -3295, -1042, +104, +2016, +289, -589, -954, -1505, -917, -2153, -1097, -1071, -1185, -298, +285, -530, -966, -69, -449, },
|
||||
{ +20202, +8717, -10102, +5865, +7549, -3841, +390, +11057, -6161, -10160, +2346, +4324, -998, -3105, -1402, -423, +2652, +520, -1022, -905, -1505, -1084, -2016, -1271, -995, -1072, -334, +432, -570, -1222, +2, -286, },
|
||||
{ +22529, +8456, -11824, +5892, +7303, -4055, +930, +11197, -5866, -10497, +2199, +4298, -1350, -2705, -1409, -1011, +2979, +873, -1590, -561, -1535, -1262, -1879, -1318, -879, -952, -350, +491, -720, -1269, +134, -272, },
|
||||
{ +23974, +9134, -13513, +5844, +7038, -4057, +615, +11611, -4790, -11317, +1983, +4325, -1634, -2352, -1084, -1939, +3123, +1383, -1969, -242, -1583, -1428, -1611, -1435, -861, -839, -383, +423, -787, -1337, +408, -512, },
|
||||
{ +25044, +9817, -14614, +5833, +6675, -4269, -71, +12212, -3614, -11890, +1484, +4440, -1786, -1990, -859, -2687, +2944, +1877, -2005, -175, -1440, -1495, -1418, -1531, -909, -716, -428, +289, -816, -1306, +466, -753, },
|
||||
{ +26536, +9314, -14645, +5829, +6191, -4884, -796, +12908, -2788, -12074, +1081, +4219, -1691, -1613, -960, -3033, +2556, +2030, -1647, -335, -1214, -1268, -1603, -1435, -985, -580, -535, +122, -796, -1260, +177, -737, },
|
||||
{ +27183, +9606, -14376, +5620, +5875, -5416, -2052, +13586, -1628, -12058, +571, +3654, -1110, -1358, -1213, -3155, +2048, +1957, -895, -643, -907, -799, -1977, -1199, -984, -549, -700, +25, -836, -1353, +35, -751, },
|
||||
{ +27148, +10286, -13645, +5373, +5534, -6103, -3440, +14036, -378, -11705, -177, +2878, -226, -1089, -1747, -3125, +1710, +1453, +80, -816, -863, -89, -2314, -1012, -907, -744, -724, -326, -1037, -1054, -249, -864, },
|
||||
{ +26584, +11121, -12486, +5232, +5105, -6806, -4740, +13839, +1078, -11022, -1066, +2034, +722, -813, -2405, -2897, +1499, +637, +937, -473, -1208, +661, -2238, -1145, -718, -867, -1215, -685, -932, -951, -292, -941, },
|
||||
{ +25805, +11950, -11123, +5119, +4637, -7115, -6017, +12797, +2771, -10082, -1948, +1322, +1341, -452, -2821, -2640, +1355, -222, +1382, +402, -1584, +1029, -1649, -1467, -434, -1354, -1712, -685, -1153, -608, -115, -1081, },
|
||||
{ +24754, +12672, -9499, +5075, +3967, -6831, -7500, +11069, +4769, -9261, -2671, +911, +1238, +136, -2811, -2628, +1336, -1074, +1459, +1369, -1523, +816, -817, -1570, -995, -1254, -2045, -954, -1214, -280, +201, -1144, },
|
||||
{ +23625, +13743, -8416, +5130, +3177, -5952, -8511, +8195, +6835, -8201, -3226, +701, +799, +463, -2132, -2701, +1161, -1431, +1042, +2182, -1162, +681, -474, -1699, -1321, -1011, -2142, -1212, -1223, -47, +480, -1192, },
|
||||
{ +22165, +15115, -7785, +5546, +2568, -5733, -8429, +4589, +8489, -6649, -4078, +663, +623, +133, -953, -2723, +670, -1020, -128, +2751, -525, +300, -532, -1412, -1518, -711, -1869, -1605, -920, -465, +529, -917, },
|
||||
{ +22262, +14424, -6906, +6480, +1678, -6039, -7848, +2265, +8205, -4418, -5059, +773, +1015, -648, +127, -2327, +238, -660, -1047, +2455, +302, -278, -464, -941, -1372, -464, -1422, -1493, -1408, -881, +595, -495, },
|
||||
{ +21426, +14579, -5798, +6478, +1251, -5676, -7497, -360, +8046, -2301, -6017, +896, +1618, -1474, +1197, -1714, -587, -84, -1747, +1143, +1103, -253, -569, -193, -1153, -2, -1077, -1593, -1678, -1403, +210, -122, },
|
||||
{ +19944, +15360, -4754, +5956, +1176, -5025, -7238, -2954, +7860, -653, -6260, +621, +2153, -1709, +1912, -1547, -1249, +742, -2688, +57, +1079, +401, -432, +447, -710, -64, -484, -1682, -1600, -2206, -540, +94, },
|
||||
{ +20147, +14018, -3775, +6372, +90, -4104, -7066, -4495, +7159, +825, -6523, +620, +3112, -2199, +2066, -1581, -1430, +939, -2947, -332, +348, +613, +1047, +312, -702, +618, -378, -1616, -1651, -2440, -1434, -218, },
|
||||
{ +19984, +13391, -3853, +7072, -294, -4065, -6337, -5631, +5959, +2227, -6012, -65, +4276, -2601, +1149, -793, -2128, +1041, -2372, -946, +540, +152, +1289, +1452, -1002, +964, +82, -1959, -1450, -2309, -2165, -747, },
|
||||
{ +20430, +12454, -5495, +8998, +65, -5776, -5336, -5251, +4459, +2909, -5003, -707, +4833, -2860, -19, +113, -2229, +469, -1412, -634, +320, -217, +361, +2809, -484, +184, +1137, -2340, -1770, -1410, -2528, -1555, },
|
||||
{ +21692, +10456, -7798, +12335, +68, -8763, -3715, -3473, +2378, +2906, -3129, -2190, +5102, -2356, -1660, +915, -1539, -297, -715, +276, -315, -459, +27, +2282, +814, +228, +658, -1653, -2043, -1236, -2088, -2094, },
|
||||
{ +22414, +8463, -8975, +14734, -856, -9903, -2019, -2434, +827, +2536, -1717, -3048, +4909, -1972, -2551, +1285, -549, -637, -715, +674, -143, -626, -627, +1991, +1177, +722, +464, -1119, -1865, -1815, -1391, -1829, },
|
||||
{ +22530, +6646, -9511, +16483, -1322, -11024, -644, -496, -1479, +1598, +531, -3996, +3822, -711, -2705, +845, +188, -390, -681, +346, -68, -65, -1383, +1684, +1452, +284, +1178, -797, -2128, -1411, -1375, -1644, },
|
||||
{ +21322, +5400, -8796, +17260, -1500, -11305, +474, +113, -2907, +1438, +1455, -4669, +3322, +481, -2925, +742, +471, -590, -202, +63, -265, +281, -1317, +1179, +1229, +431, +1123, -441, -2004, -992, -1149, -2020, },
|
||||
{ +19175, +5030, -7462, +16622, -929, -10530, +416, +343, -3120, +766, +1767, -4343, +2822, +1079, -2638, +519, +570, -563, -289, +165, -315, +82, -803, +997, +763, +599, +901, -468, -1286, -794, -1066, -1838, },
|
||||
{ +16819, +5384, -6370, +14965, +290, -9700, +208, +612, -3392, +183, +2183, -3797, +2080, +1803, -2493, -16, +1023, -550, -707, +329, -153, -121, -550, +1037, +727, +224, +746, -397, -1126, -361, -1019, -1629, },
|
||||
{ +14912, +6709, -4328, +10591, -498, -7109, +111, -741, -770, +328, -733, -960, +2191, +30, -1474, -311, +350, -380, -1280, +108, +235, +180, +74, +223, +54, +624, +423, -99, +35, -79, -285, -105, },
|
||||
{ +12603, +6186, -2629, +9885, -405, -6060, +108, -976, -537, +418, -737, -896, +2184, +171, -1454, -339, +212, -324, -1080, +111, +88, +93, +170, +250, +211, +621, +320, -131, +116, -1, -403, -106, },
|
||||
{ +10408, +5748, -848, +8724, -167, -4613, -400, -1217, -70, +524, -929, -678, +2111, +202, -1239, -402, +223, -337, -970, +153, +57, +16, +164, +314, +375, +524, +121, +6, +131, -34, -146, -32, },
|
||||
{ +8810, +5752, -110, +7195, +525, -3549, -713, -1201, +150, +589, -802, -524, +1697, +366, -971, -531, +153, -232, -820, +124, +74, +31, +92, +425, +518, +160, +114, +336, +59, -121, +192, +176, },
|
||||
{ +7707, +5602, +35, +6275, +1122, -3180, -685, -740, +80, +415, -374, -403, +1135, +566, -689, -718, +117, -57, -675, +45, +161, +35, +47, +597, +362, +73, +358, +355, +34, +128, +414, +95, },
|
||||
{ +7097, +5535, -192, +5388, +1340, -2724, -316, -506, -92, +415, +68, -445, +787, +694, -670, -663, +194, +20, -606, +105, +221, -83, +318, +654, +165, +211, +501, +464, +163, +305, +282, -143, },
|
||||
{ +6452, +5181, -188, +4851, +1397, -2250, -47, -387, -147, +524, +273, -528, +602, +655, -502, -522, +157, +53, -489, +203, +143, +158, +611, +321, +242, +571, +633, +422, +125, +234, -5, -218, },
|
||||
{ +5779, +4631, +28, +4565, +1363, -1816, +67, -196, +8, +488, +200, -488, +649, +572, -444, -381, +184, +6, -287, +343, +218, +465, +530, +350, +563, +605, +532, +190, +1, +83, -90, -218, },
|
||||
{ +5208, +4384, +145, +3924, +1403, -1285, +290, -147, -75, +540, +306, -396, +492, +507, -283, -289, +166, +176, +48, +344, +475, +764, +468, +481, +475, +299, +347, +79, -73, +8, -92, -335, },
|
||||
{ +4512, +3969, +359, +3651, +1556, -960, +277, -42, +99, +522, +205, -303, +469, +444, -138, -122, +272, +416, +283, +588, +781, +710, +302, +187, +175, +216, +260, +77, -143, -145, -168, -400, },
|
||||
{ +3801, +3629, +701, +3369, +1505, -562, +487, -64, +147, +580, +155, -276, +520, +581, +54, +7, +535, +858, +532, +711, +669, +180, +6, +159, +92, +196, +192, -93, -202, -135, -272, -498, },
|
||||
{ +3442, +3530, +703, +2812, +1660, -44, +424, -101, +187, +562, +336, -54, +523, +647, +416, +546, +884, +888, +356, +303, +242, +72, +10, +71, +14, +72, +66, -163, -241, -221, -397, -501, },
|
||||
{ +2690, +3000, +1279, +2876, +1515, +205, +493, -52, +442, +658, +264, +192, +1000, +1052, +635, +643, +744, +413, +42, +208, +138, +112, -80, -143, -63, -30, +7, -171, -300, -327, -429, -392, },
|
||||
{ +2478, +3269, +1270, +2133, +1692, +521, +531, +45, +514, +1060, +829, +686, +1118, +1030, +491, +45, +183, +420, +250, +132, -125, -159, -155, -121, -114, -108, -174, -265, -205, -328, -363, -352, },
|
||||
{ +1870, +2687, +1568, +2459, +1988, +651, +612, +481, +1093, +1477, +1114, +836, +591, +278, +48, +164, +470, +379, -76, -182, +38, -195, -357, -228, -185, -143, -188, -137, -211, -282, -321, -361, },
|
||||
{ +2002, +3298, +2072, +1782, +1264, +1387, +2004, +1233, +1029, +1042, +338, -15, +176, +284, +423, +207, -57, +116, -13, -210, -269, -453, -350, -132, -143, -177, -143, -97, -213, -359, -341, -306, },
|
||||
{ +2146, +3307, +2355, +2752, +2119, +1859, +2021, +710, +97, +234, +110, +124, +235, +191, +340, -11, -121, +97, -245, -337, -346, -487, -277, -43, -78, -145, -151, -149, -204, -284, -366, -347, },
|
||||
{ +2369, +3977, +3947, +3602, +1697, +1277, +778, -51, +139, +274, +157, +44, -14, +65, +388, -56, -226, -108, -386, -368, -349, -275, -166, -135, -161, -68, -48, -184, -269, -324, -303, -300, },
|
||||
{ +2991, +5611, +4893, +2413, +32, +530, +1206, +716, -81, -249, -72, +167, +231, -189, +96, +62, -289, -321, -341, -226, -287, -249, -289, -147, -48, -120, -84, -169, -256, -385, -287, -211, },
|
||||
{ +3480, +5919, +3215, +1230, +711, +1536, +1856, +445, -348, +146, +147, -235, +202, -60, -65, -10, -330, -145, -168, -321, -230, -233, -366, -152, -34, -139, -131, -161, -196, -267, -285, -233, },
|
||||
{ +2004, +3234, +1971, +1898, +2099, +2208, +2155, +993, +558, +802, +440, -18, -153, -3, +129, -234, -279, -55, -99, -109, -245, -275, -242, -234, -224, -178, -205, -148, -89, -176, -206, -280, },
|
||||
{ +1279, +2318, +1457, +1126, +1665, +2069, +1778, +1033, +1441, +1718, +1054, +741, +475, +244, +79, -171, -166, -218, -231, -69, -145, -125, -126, -248, -153, -180, -287, -235, -255, -196, -120, -109, },
|
||||
{ +1453, +2492, +1452, +1154, +1435, +1406, +842, +362, +1287, +1720, +1252, +1179, +1008, +731, +566, +64, -227, -101, -154, -275, -230, -121, -97, -71, -157, -175, -212, -268, -250, -286, -220, -166, },
|
||||
{ +1879, +2842, +1125, +1084, +1653, +1253, +390, -18, +973, +1151, +667, +1023, +1287, +1158, +966, +422, +174, +136, -200, -125, -209, -336, -116, -116, -96, -37, -181, -264, -245, -282, -257, -233, },
|
||||
{ +2149, +3242, +1318, +1052, +1535, +1017, +118, +168, +1096, +645, +127, +653, +733, +907, +1209, +756, +503, +444, +179, +76, -160, -223, -173, -251, -109, -40, -121, -158, -228, -282, -247, -243, },
|
||||
{ +2317, +3435, +1467, +1216, +1790, +944, -476, +147, +1327, +465, +25, +491, +329, +437, +651, +458, +687, +794, +439, +296, +209, +11, -156, -194, -142, -157, -160, -145, -173, -218, -241, -271, },
|
||||
{ +2632, +3865, +1576, +1194, +1850, +818, -747, +351, +1336, +91, -24, +570, +326, +220, +377, +87, +100, +501, +607, +596, +401, +255, +179, -41, -117, -38, -173, -278, -200, -177, -186, -241, },
|
||||
{ +2913, +4307, +1690, +1174, +1997, +721, -1042, +507, +1361, -158, -88, +596, +317, +156, +293, -13, -74, +80, +115, +344, +456, +579, +399, +152, +182, +78, -95, -162, -227, -292, -160, -212, },
|
||||
{ +3419, +4851, +1663, +1035, +2035, +628, -1177, +737, +1236, -536, -64, +690, +334, +51, +172, -13, -135, -42, -10, -2, -68, +347, +513, +426, +347, +211, +168, -24, -193, -236, -186, -272, },
|
||||
{ +3767, +5304, +1782, +1016, +2102, +463, -1378, +1048, +1239, -936, -179, +746, +362, +62, +89, -105, -187, -70, -33, -113, -245, -31, +98, +248, +442, +396, +305, +172, +12, -165, -166, -183, },
|
||||
{ +4285, +5774, +1650, +1174, +2192, +50, -1405, +1467, +1118, -1334, -217, +829, +333, +12, +50, -124, -225, -140, -22, -146, -307, -135, -82, -31, +86, +236, +387, +312, +118, +25, +51, -123, },
|
||||
{ +4848, +6188, +1491, +1445, +2261, -490, -1401, +1942, +965, -1671, -300, +836, +385, -47, +8, -178, -306, -200, +51, -186, -351, -153, -292, -30, +25, -103, +13, +150, +219, +188, +112, +17, },
|
||||
{ +5454, +6649, +1314, +1669, +2321, -943, -1366, +2334, +803, -1890, -325, +781, +385, -83, +115, -228, -488, -225, +76, -126, -377, -187, -365, -53, +30, -174, -150, -173, -42, +89, +185, +106, },
|
||||
{ +5926, +7107, +1339, +1747, +2383, -1252, -1530, +2755, +780, -2246, -303, +735, +323, -85, +73, -145, -589, -384, +135, -187, -469, -43, -386, -191, +46, -215, -180, -236, -326, -211, +7, +83, },
|
||||
{ +6333, +7526, +1384, +2014, +2467, -1782, -1590, +3195, +714, -2494, -349, +719, +280, -145, +55, -69, -636, -504, +150, -221, -574, -43, -314, -228, -37, -138, -204, -369, -416, -299, -97, -205, },
|
||||
{ +6696, +8017, +1498, +2006, +2624, -2005, -1798, +3417, +748, -2614, -432, +706, +173, -173, +53, -56, -573, -718, +132, -67, -717, -164, -301, -196, -38, -159, -197, -465, -469, -327, -185, -358, },
|
||||
{ +6930, +8687, +1750, +1599, +2863, -1765, -2210, +3311, +900, -2491, -605, +596, +191, -265, +91, -35, -551, -808, +27, +34, -661, -338, -391, -71, -59, -282, -167, -426, -499, -386, -346, -349, },
|
||||
{ +7032, +9112, +2294, +1319, +2928, -1295, -2725, +3058, +1141, -2349, -736, +409, +218, -319, +37, -5, -474, -896, -105, +108, -719, -327, -414, -210, -8, -342, -280, -290, -610, -529, -264, -398, },
|
||||
{ +7290, +9148, +2788, +1517, +2890, -1004, -3145, +2936, +1182, -2207, -713, +170, +226, -310, -137, +66, -236, -1052, -288, +218, -703, -396, -407, -298, -61, -288, -332, -407, -637, -624, -99, -427, },
|
||||
{ +8295, +8777, +2246, +2812, +2858, -1707, -2793, +3041, +460, -1834, -416, -196, +229, -361, -255, +143, -68, -1152, -427, +236, -539, -557, -489, -199, -168, -280, -301, -623, -729, -417, -190, -475, },
|
||||
{ +8813, +8385, +2070, +3791, +3408, -2249, -3089, +3581, -338, -1829, +311, -486, -198, -209, -270, -126, +417, -1313, -692, +394, -483, -630, -627, -124, -246, -308, -391, -642, -737, -489, -253, -480, },
|
||||
{ +9480, +7764, +1344, +5665, +3938, -3530, -2582, +4359, -1779, -1905, +1349, -610, -594, -360, -91, -401, +691, -1075, -1088, +435, -349, -619, -660, -124, -373, -324, -465, -444, -823, -782, -95, -634, },
|
||||
{ +10219, +7759, +108, +6635, +4903, -4328, -2227, +5026, -3212, -2275, +2513, -596, -1046, -594, +196, -690, +587, -537, -1473, +302, -113, -638, -684, -100, -439, -459, -480, -472, -766, -889, -314, -724, },
|
||||
{ +11359, +8080, -1537, +6725, +6202, -4589, -2055, +5443, -4331, -2746, +3483, -358, -1535, -803, +500, -787, +107, +0, -1473, -127, +202, -644, -687, +80, -605, -513, -488, -741, -549, -859, -768, -615, },
|
||||
{ +12561, +8515, -3181, +6443, +7273, -4422, -1771, +5642, -5238, -3343, +4113, +235, -1988, -1143, +783, -757, -401, +173, -1070, -614, +288, -431, -703, +306, -751, -608, -594, -870, -585, -862, -824, -695, },
|
||||
{ +13637, +9078, -4327, +5307, +8230, -3683, -1700, +6016, -6052, -3983, +4365, +989, -2192, -1647, +1018, -629, -785, -62, -507, -850, +26, -33, -720, +529, -810, -802, -657, -1006, -784, -809, -610, -865, },
|
||||
{ +14620, +9720, -5196, +3985, +8621, -2703, -1406, +6384, -6867, -4537, +4323, +1689, -2163, -2167, +1076, -438, -1009, -536, -136, -784, -345, +300, -530, +532, -827, -847, -766, -1246, -914, -773, -428, -804, },
|
||||
{ +16072, +9930, -6183, +3360, +8255, -2295, -119, +6470, -7878, -4616, +4153, +2063, -2029, -2471, +1020, -386, -950, -1040, -67, -497, -520, +425, -98, +296, -886, -669, -1038, -1392, -1030, -835, -225, -497, },
|
||||
{ +17538, +10139, -7275, +3167, +7496, -2312, +1507, +6655, -8887, -4746, +4122, +2161, -1895, -2506, +749, -326, -908, -1248, -345, -284, -377, +510, +186, +31, -841, -583, -1261, -1430, -1259, -849, -50, -134, },
|
||||
{ +18795, +10599, -8283, +2946, +6835, -2703, +2875, +7278, -9742, -5079, +4325, +2043, -1829, -2306, +376, -514, -590, -1442, -695, -362, +63, +785, +55, -22, -629, -692, -1264, -1504, -1464, -809, +17, +195, },
|
||||
{ +19828, +11378, -9267, +2629, +6374, -3347, +3612, +8402, -10339, -5537, +4515, +1859, -1884, -1929, -100, -800, -262, -1561, -976, -577, +410, +1421, -419, +34, -250, -981, -1080, -1605, -1632, -802, +11, +380, },
|
||||
{ +21206, +11505, -9851, +2389, +6035, -4347, +4232, +9433, -10759, -5759, +4577, +1581, -1877, -1583, -452, -1192, +46, -1648, -1025, -873, +537, +2263, -927, +52, +224, -1290, -882, -1564, -1935, -752, -159, +454, },
|
||||
{ +21951, +12180, -10180, +1892, +5955, -5226, +3946, +10828, -10791, -5984, +4342, +1308, -1588, -1445, -711, -1558, +306, -1741, -844, -1199, +362, +3030, -1015, -204, +814, -1397, -873, -1354, -2329, -849, -416, +417, },
|
||||
{ +22506, +12779, -10294, +1612, +5688, -5970, +3299, +11834, -10475, -5915, +3858, +872, -1070, -1438, -1116, -1691, +435, -1836, -594, -1442, -11, +3401, -605, -538, +1220, -1085, -1193, -1113, -2738, -1072, -644, +153, },
|
||||
{ +23186, +13097, -10391, +2031, +4838, -6522, +2825, +12019, -9671, -5661, +3239, +340, -378, -1439, -1649, -1540, +599, -1995, -278, -1529, -462, +3340, +152, -492, +1137, -401, -1605, -1222, -2820, -1325, -987, -68, },
|
||||
{ +22882, +14119, -9689, +1389, +4734, -6999, +1524, +12257, -8386, -5521, +2330, +28, +188, -1505, -1990, -1510, +944, -2230, -172, -1187, -1194, +2809, +1343, -197, +605, +402, -2099, -1365, -2620, -1824, -1038, -503, },
|
||||
{ +21870, +15422, -8022, +32, +4928, -6961, -638, +12423, -6481, -5741, +1679, -358, +537, -1263, -2316, -1338, +1170, -2350, -127, -788, -1648, +1934, +2335, +603, -234, +775, -1895, -1692, -2402, -2007, -1134, -816, },
|
||||
{ +21377, +15848, -6611, -135, +4095, -6589, -2340, +11613, -4347, -6089, +1401, -892, +589, -722, -2599, -1204, +1299, -2460, -64, -474, -1793, +1309, +2278, +1468, -710, +729, -1381, -1948, -2200, -1989, -1059, -1104, },
|
||||
{ +21825, +15338, -6288, +1723, +2245, -6807, -2394, +9743, -2760, -5631, +785, -1266, +822, -455, -2395, -1291, +1244, -2162, -287, +63, -1716, +759, +1340, +2033, -170, +136, -645, -1936, -2205, -1413, -1181, -1687, },
|
||||
{ +22088, +14986, -6294, +3940, +475, -7388, -1931, +7563, -1485, -4751, -302, -1228, +1282, -606, -1733, -1685, +1011, -1322, -834, +679, -1560, -371, +652, +2168, +634, +122, -548, -1338, -1967, -1463, -1230, -1785, },
|
||||
{ +21218, +15763, -6154, +5244, -207, -8203, -1741, +5343, +1, -4070, -1324, -1042, +1940, -1042, -1041, -1719, +173, +234, -1546, +421, -980, -1263, -189, +2163, +1362, +537, -295, -894, -1836, -1649, -862, -1860, },
|
||||
{ +20943, +15351, -5193, +5824, -681, -8421, -2182, +3733, +912, -3280, -2125, -629, +2393, -1338, -793, -1272, -318, +619, -1449, -542, -613, -929, -1049, +1622, +1869, +1834, -606, -1014, -899, -1858, -1052, -1583, },
|
||||
{ +20467, +15036, -4299, +6074, -972, -8163, -2993, +2103, +1953, -2525, -2906, +84, +2478, -1496, -381, -1209, -661, +522, -1431, -1137, -494, -29, -1724, +1410, +1857, +2078, +198, -1222, -492, -1845, -1016, -1459, },
|
||||
{ +19997, +14492, -3439, +6231, -1292, -7499, -3870, +428, +3016, -1672, -3589, +505, +3009, -1682, -424, -1271, -888, +168, -1633, -640, -998, +512, -655, +572, +1038, +2022, +1564, -1436, -837, -970, -1064, -1590, },
|
||||
{ +19121, +13891, -1956, +5864, -1769, -5712, -5524, -1209, +4728, -1224, -4384, +1529, +2886, -1835, -216, -2037, -717, +368, -2459, +62, -470, -20, +501, +397, +27, +1794, +1472, -406, -774, -1035, -530, -1651, },
|
||||
{ +18795, +12787, -866, +5679, -2785, -3778, -6009, -3416, +6104, -148, -5120, +1968, +2942, -2232, -361, -2334, -318, +255, -2810, +772, +147, -722, +931, +1048, -622, +926, +926, +464, +8, -1388, -195, -1234, },
|
||||
{ +19769, +11583, -2448, +6778, -3321, -3974, -5178, -3690, +5661, +410, -4626, +1587, +2861, -2687, -704, -1965, -264, +354, -2735, +728, +431, -372, +846, +890, -556, +313, +642, +248, +534, -654, -743, -572, },
|
||||
{ +20742, +9918, -4333, +9177, -3673, -5562, -3412, -3017, +4558, +903, -4422, +932, +3308, -2821, -1440, -1193, +107, +341, -2476, +352, +665, +234, +482, +681, -223, -107, +450, +161, +378, +311, -786, -592, },
|
||||
{ +20393, +8513, -5551, +11591, -3341, -7618, -1960, -1344, +2974, +225, -3410, +284, +3332, -2616, -1768, -458, +23, +66, -1947, +502, +187, +334, +642, +553, -324, +98, +28, -212, +831, +137, -320, -307, },
|
||||
{ +19863, +7195, -5891, +13080, -3237, -8975, -179, -735, +1159, +597, -2780, -566, +3377, -1932, -1835, -26, +57, -431, -1301, +563, -286, +265, +803, +515, -423, +180, +122, -246, +644, +165, -191, +77, },
|
||||
{ +18658, +6850, -5751, +12388, -2703, -7823, +255, -1504, +610, +770, -2248, -983, +3161, -1268, -1738, +128, -21, -478, -1161, +338, -70, +251, +391, +591, -323, +20, +528, -36, +54, +115, -36, +34, },
|
||||
{ +17076, +6736, -5238, +11355, -1525, -7217, -40, -1078, -147, +458, -1371, -990, +2637, -628, -1439, -189, +204, -339, -1390, +203, +285, +214, +63, +453, -132, +311, +558, +23, -66, -152, +62, -43, },
|
||||
{ +13623, +7663, -736, +5443, -2593, -2244, -1530, -1649, +1729, -32, -2266, +999, +1978, -1230, -677, -1107, +219, -259, -1521, +393, -253, -340, +340, +425, +603, +1120, -212, -683, -191, -585, -148, +387, },
|
||||
{ +11820, +7583, -426, +4965, -1356, -2529, -1167, -1245, +983, +124, -1778, +794, +1769, -842, -700, -980, +259, -378, -1380, +412, -355, -376, +513, +422, +537, +830, -258, -264, -158, -658, +7, +383, },
|
||||
{ +10321, +7312, -105, +4864, -813, -2658, -650, -1140, +594, +154, -1345, +747, +1470, -560, -589, -773, +109, -428, -1061, +264, -409, -228, +464, +450, +557, +455, -229, +107, -87, -614, +63, +459, },
|
||||
{ +8832, +6788, +754, +4529, -737, -2058, -537, -1141, +593, +102, -1073, +689, +1331, -427, -481, -535, -57, -390, -871, +136, -280, -172, +355, +496, +482, +211, +7, +299, +18, -389, +32, +568, },
|
||||
{ +7264, +6341, +1804, +3830, -547, -1150, -702, -1198, +746, +193, -1033, +629, +1243, -340, -243, -463, -211, -272, -673, +38, -232, -89, +255, +491, +349, +237, +305, +440, +181, -306, +96, +481, },
|
||||
{ +5764, +5793, +2760, +3310, -250, -431, -863, -1206, +942, +277, -1025, +490, +1124, +11, -135, -654, -133, +3, -652, -89, -115, -120, +324, +473, +214, +489, +656, +578, +94, -300, +162, +271, },
|
||||
{ +4877, +5432, +2934, +2887, +9, -98, -665, -1051, +773, +328, -754, +397, +892, +204, -54, -717, +22, +137, -707, -136, -8, -16, +384, +457, +358, +804, +826, +406, -152, -241, +83, +160, },
|
||||
{ +4273, +4974, +2996, +2688, +19, +236, -317, -1082, +645, +529, -499, +171, +725, +487, -28, -702, +171, +109, -663, +33, +8, +168, +685, +509, +520, +907, +639, +123, -222, -249, +142, +242, },
|
||||
{ +3881, +4471, +2801, +2721, +214, +232, -276, -784, +726, +443, -495, +187, +790, +514, -89, -624, +292, +140, -590, +200, +269, +545, +770, +316, +532, +788, +265, -82, -82, -100, +41, +29, },
|
||||
{ +3766, +4300, +2408, +2391, +333, +347, -106, -602, +651, +407, -345, +364, +720, +250, +52, -284, +220, +186, -143, +547, +529, +560, +390, +206, +482, +471, +188, -7, -119, -170, +39, -9, },
|
||||
{ +3436, +3912, +2322, +2385, +450, +371, -77, -417, +729, +455, -307, +332, +707, +317, +188, -91, +487, +565, +170, +463, +431, +296, +60, +230, +424, +421, +106, -107, -113, -167, +28, -121, },
|
||||
{ +3183, +3685, +2212, +2271, +537, +324, +19, -108, +778, +330, -270, +582, +667, +327, +729, +405, +569, +537, +26, +242, +252, -24, +148, +398, +218, +251, +64, -101, -137, -167, -61, -180, },
|
||||
{ +2975, +3602, +2238, +1946, +368, +615, +350, -208, +659, +572, -66, +604, +1024, +904, +959, +291, +202, +264, -144, +133, +221, +2, +109, +243, +252, +133, -64, -129, -180, -137, -144, -273, },
|
||||
{ +2802, +3321, +2156, +2179, +535, +317, +283, +375, +791, +445, +493, +1260, +1224, +697, +585, -84, -52, +130, -53, +215, +5, -34, +157, +233, +121, -41, -96, -197, -124, -177, -272, -304, },
|
||||
{ +2567, +3351, +2539, +1843, +97, +744, +689, +452, +1235, +1224, +767, +951, +804, +213, +156, -203, +95, +139, -182, +93, +76, +25, +66, +145, -99, -130, -124, -168, -143, -326, -266, -289, },
|
||||
{ +2718, +3449, +2308, +1797, +208, +761, +1522, +1644, +1223, +789, +460, +314, +454, -11, +113, +21, -170, +13, +13, +85, +59, -34, -70, +29, -183, -196, -119, -202, -174, -262, -320, -243, },
|
||||
{ +2540, +3343, +2399, +2066, +1062, +1501, +1604, +1736, +1082, -140, -197, +490, +498, -164, -13, -43, -88, +38, +21, +50, -52, -74, -79, -137, -262, -173, -156, -121, -156, -298, -206, -189, },
|
||||
{ +2234, +3727, +3392, +2599, +1296, +1388, +1575, +990, +120, +142, -3, +141, +343, -173, +83, -53, -176, +87, -65, -57, -14, -108, -214, -261, -285, -100, -90, -157, -152, -234, -209, -204, },
|
||||
{ +2985, +5564, +3761, +1458, +757, +1364, +784, +451, +634, -7, -242, +3, +522, +51, -283, +33, -103, -187, +6, +14, -99, -195, -315, -245, -150, -121, -75, -132, -198, -215, -225, -243, },
|
||||
{ +3536, +5654, +3321, +1733, +414, +394, +1272, +1090, -2, -98, +317, +30, +224, +141, -65, -151, -228, -176, -28, +9, -112, -143, -326, -205, -121, -146, -154, -155, -169, -220, -257, -233, },
|
||||
{ +2423, +4490, +3856, +2307, +306, +542, +1485, +1042, +272, +121, +367, +345, +305, +76, +54, -98, -197, -211, -235, -76, +30, -102, -249, -110, -163, -144, -218, -256, -176, -211, -237, -223, },
|
||||
{ +2207, +4508, +3055, +1310, +930, +958, +1117, +900, +798, +352, +193, +679, +614, +197, +54, +57, -131, -330, -215, -106, -182, -79, -43, -101, -76, -110, -233, -306, -312, -262, -203, -241, },
|
||||
{ +2155, +4216, +2568, +1005, +1213, +966, +459, +898, +1067, +401, +559, +752, +475, +636, +473, -23, -19, +1, -327, -285, -98, -96, -180, -15, +129, -115, -171, -209, -363, -372, -286, -209, },
|
||||
{ +2070, +3883, +2353, +1160, +1380, +728, +99, +912, +928, +215, +528, +873, +808, +593, +419, +448, +247, -17, -66, -137, -244, -204, -83, -38, -42, +64, -18, -242, -235, -315, -412, -310, },
|
||||
{ +2125, +3872, +2333, +1117, +1382, +652, -82, +932, +929, -32, +165, +810, +760, +591, +704, +380, +171, +402, +223, -173, -55, +18, -230, -138, +84, +9, -127, -88, -152, -290, -286, -341, },
|
||||
{ +2204, +3829, +2303, +1430, +1528, +266, -259, +1194, +944, -279, +37, +705, +496, +385, +633, +421, +252, +297, +317, +192, +43, +66, +57, +9, -58, -33, -24, -181, -247, -150, -214, -284, },
|
||||
{ +2377, +4116, +2337, +1342, +1538, +248, -324, +1198, +907, -340, -9, +552, +446, +300, +373, +259, +71, +202, +429, +213, +40, +308, +228, +122, +212, +69, -122, -224, -230, -240, -255, -174, },
|
||||
{ +2547, +4334, +2430, +1433, +1566, +87, -418, +1351, +878, -486, -33, +547, +363, +146, +403, +264, -240, -131, +304, +205, +66, +265, +312, +373, +311, +248, +138, -166, -373, -314, -205, -258, },
|
||||
{ +2830, +4723, +2442, +1353, +1596, +57, -445, +1370, +794, -551, +26, +483, +302, +84, +326, +353, -232, -336, -50, +55, -20, +99, +386, +443, +376, +430, +300, -2, -200, -321, -318, -255, },
|
||||
{ +3101, +5094, +2484, +1317, +1620, -48, -486, +1479, +713, -731, +26, +531, +246, -26, +207, +395, -120, -435, -199, -118, -261, -82, +201, +404, +445, +420, +397, +148, -80, -175, -230, -288, },
|
||||
{ +3513, +5583, +2388, +1169, +1692, -102, -510, +1532, +632, -825, +39, +484, +162, -19, +107, +357, -62, -406, -271, -220, -305, -288, -44, +301, +381, +318, +417, +216, -33, -33, -125, -197, },
|
||||
{ +3971, +6063, +2297, +1049, +1754, -235, -496, +1678, +453, -941, +114, +521, +25, -141, +100, +323, -30, -393, -314, -245, -341, -357, -157, +129, +280, +188, +259, +194, -7, -19, -69, -59, },
|
||||
{ +4462, +6601, +2162, +929, +1825, -406, -449, +1847, +245, -1033, +198, +531, -55, -243, +34, +274, -46, -317, -280, -344, -378, -331, -147, -22, +200, +127, +86, +97, -172, -74, +29, -12, },
|
||||
{ +4929, +7134, +2067, +832, +1905, -671, -420, +2107, +13, -1176, +285, +566, -115, -373, -56, +231, -88, -317, -242, -369, -432, -373, -60, -90, +32, +212, -22, -102, -274, -226, -80, -18, },
|
||||
{ +5380, +7722, +2007, +689, +2018, -933, -447, +2383, -171, -1301, +369, +562, -128, -427, -220, +187, -78, -332, -233, -409, -419, -408, -34, +5, -132, +185, +51, -252, -327, -342, -282, -98, },
|
||||
{ +5728, +8332, +2054, +461, +2152, -1109, -551, +2612, -297, -1395, +446, +591, -229, -430, -303, +22, -78, -336, -214, -416, -470, -471, -25, +180, -147, -54, +113, -193, -423, -380, -487, -230, },
|
||||
{ +6113, +8894, +2133, +278, +2235, -1274, -667, +2769, -471, -1368, +504, +562, -301, -464, -281, -179, -193, -277, -163, -442, -520, -538, -26, +230, -9, -150, -82, -60, -486, -442, -508, -419, },
|
||||
{ +6822, +9262, +1874, +539, +2127, -1579, -524, +2787, -765, -1217, +595, +512, -360, -580, -126, -403, -358, -224, -134, -389, -601, -579, +2, +211, +8, +23, -240, -219, -412, -487, -530, -498, },
|
||||
{ +7515, +9472, +1708, +950, +1940, -1744, -475, +2685, -890, -1100, +601, +523, -322, -777, -22, -467, -558, -250, -104, -316, -558, -673, -18, +285, -56, +161, -229, -389, -420, -494, -565, -489, },
|
||||
{ +7940, +9637, +1858, +1251, +1898, -1722, -763, +2646, -912, -1044, +619, +459, -176, -902, -97, -399, -684, -474, -18, -221, -503, -687, -201, +491, -97, +79, -100, -413, -500, -584, -564, -465, },
|
||||
{ +8437, +9625, +1884, +1715, +2059, -1879, -1105, +2725, -1064, -1040, +741, +338, -154, -826, -248, -438, -648, -774, -57, -33, -524, -628, -313, +497, +49, -120, -60, -285, -620, -701, -513, -517, },
|
||||
{ +9220, +9383, +1442, +2663, +2411, -2496, -1180, +2907, -1426, -1017, +899, +285, -271, -686, -239, -660, -556, -895, -297, +126, -442, -632, -203, +342, +145, -93, -144, -242, -638, -729, -500, -582, },
|
||||
{ +9906, +8999, +886, +4046, +2822, -3537, -956, +3303, -2122, -1097, +1212, +287, -466, -677, -82, -821, -584, -919, -622, +120, -110, -798, -101, +317, +50, +139, -325, -314, -494, -726, -682, -494, },
|
||||
{ +10165, +8622, +635, +5498, +3208, -4703, -409, +3974, -3274, -1343, +1812, +349, -758, -791, +168, -846, -676, -904, -937, -105, +296, -819, -186, +443, -101, +357, -353, -431, -390, -698, -845, -370, },
|
||||
{ +10778, +9125, -461, +5499, +4480, -4885, -762, +4363, -3869, -1600, +2274, +309, -1021, -799, +442, -989, -823, -718, -1054, -519, +402, -449, -376, +548, -68, +372, -259, -484, -358, -718, -919, -405, },
|
||||
{ +11808, +9598, -1631, +4810, +5752, -4539, -1177, +4391, -4229, -1790, +2570, +358, -1408, -786, +715, -1102, -1041, -581, -992, -885, +191, -44, -368, +438, +142, +323, -208, -398, -399, -804, -937, -537, },
|
||||
{ +12864, +10102, -2814, +4041, +6509, -3716, -1366, +4112, -4371, -1965, +2757, +499, -1803, -807, +909, -1173, -1234, -559, -856, -1084, -225, +261, -140, +202, +383, +376, -191, -207, -486, -953, -891, -645, },
|
||||
{ +13770, +10842, -3762, +2948, +6944, -2626, -1423, +3706, -4498, -2054, +2738, +733, -2151, -912, +1063, -1203, -1347, -631, -679, -1132, -639, +290, +229, +18, +464, +552, -134, -53, -593, -1082, -910, -725, },
|
||||
{ +14673, +11578, -4475, +1691, +7096, -1713, -1082, +3280, -4814, -1919, +2563, +861, -2306, -1111, +1114, -1240, -1354, -818, -528, -1086, -897, +19, +528, +85, +343, +790, +8, -1, -705, -1107, -997, -856, },
|
||||
{ +15287, +12615, -5054, +450, +7031, -997, -525, +3136, -5350, -1613, +2407, +806, -2180, -1353, +994, -1181, -1315, -1000, -400, -980, -938, -358, +637, +322, +261, +1020, +234, -8, -727, -1060, -1088, -991, },
|
||||
{ +16341, +13198, -5831, -183, +6631, -925, +450, +3048, -6132, -1100, +2328, +438, -1973, -1456, +665, -1224, -1147, -1229, -302, -819, -969, -597, +502, +511, +258, +1295, +390, -116, -684, -1059, -1180, -1113, },
|
||||
{ +17889, +13289, -7003, +251, +5613, -1102, +1630, +2856, -6736, -544, +2351, -201, -1577, -1545, +280, -1291, -936, -1371, -191, -616, -996, -677, +307, +583, +290, +1693, +557, -380, -523, -1129, -1279, -1128, },
|
||||
{ +19421, +13294, -8130, +984, +4304, -1338, +2588, +2708, -7031, -194, +2313, -845, -1057, -1787, -127, -1222, -940, -1254, -198, -479, -881, -843, +201, +539, +228, +2168, +760, -747, -401, -1194, -1459, -1011, },
|
||||
{ +20606, +13470, -8907, +1610, +3084, -1671, +3262, +2674, -6933, -54, +2126, -1222, -615, -2058, -501, -1073, -920, -1108, -226, -364, -678, -985, +112, +592, +23, +2550, +1067, -1061, -439, -1160, -1596, -844, },
|
||||
{ +21678, +13397, -9105, +1972, +1964, -2147, +3689, +2647, -6634, +29, +1710, -1374, -333, -2266, -901, -865, -791, -1224, -108, -330, -517, -988, -65, +848, -297, +2505, +1436, -1115, -771, -1020, -1548, -824, },
|
||||
{ +22043, +13828, -8808, +1968, +1087, -2691, +3735, +2813, -6153, -63, +1327, -1523, -22, -2413, -1231, -625, -620, -1517, +116, -296, -490, -687, -330, +1228, -654, +1883, +1897, -817, -1166, -848, -1353, -842, },
|
||||
{ +21862, +14553, -8083, +1733, +298, -3191, +3378, +3080, -5574, -270, +1014, -1790, +389, -2456, -1466, -480, -483, -1820, +297, -178, -667, -4, -656, +1283, -726, +917, +2011, -106, -1245, -999, -903, -756, },
|
||||
{ +21352, +15281, -6885, +1395, -334, -3665, +2670, +3546, -5077, -445, +803, -2146, +977, -2334, -1692, -440, -333, -2064, +440, -23, -774, +810, -1201, +1101, -364, -194, +1769, +892, -1065, -1132, -267, -995, },
|
||||
{ +20861, +15614, -5482, +1081, -870, -4341, +1898, +3930, -4851, -435, +471, -2413, +1653, -2264, -1871, -605, -244, -2109, +276, +437, -945, +994, -1311, +557, +83, -748, +1003, +1546, -293, -1147, -222, -1064, },
|
||||
{ +20341, +15837, -4243, +1301, -1450, -5105, +1347, +3961, -4522, -413, +325, -2497, +2316, -2115, -2138, -652, -297, -1870, +211, +677, -1276, +1047, -1153, -118, +720, -740, -74, +2033, +582, -1274, -145, -714, },
|
||||
{ +19636, +16119, -3188, +1683, -1865, -5991, +823, +3722, -4115, -322, +190, -2340, +2786, -2019, -2280, -876, -206, -1208, -519, +732, -1211, +541, -669, -258, +512, +140, -566, +1174, +1263, -677, -170, -802, },
|
||||
{ +18858, +16285, -2143, +1991, -1980, -6784, +50, +3403, -3485, -245, +189, -2095, +3079, -1823, -2721, -438, -196, -1206, -875, +432, -1124, +431, -376, -252, +414, +1193, -856, -296, +1960, -172, -317, -664, },
|
||||
{ +18488, +15830, -1291, +2547, -2229, -7386, -730, +2796, -2460, -104, -268, -1138, +2825, -2015, -2015, -709, -496, -920, -1536, +112, -516, +216, -578, +707, +252, +875, +136, -1180, +1044, +302, +130, -464, },
|
||||
{ +18032, +15462, -771, +3171, -2446, -7862, -1764, +2636, -1567, +5, -496, -701, +2816, -1760, -1991, -874, -746, -1167, -1739, +343, -701, +364, +15, +532, +203, +973, +810, -1570, -221, +371, +695, -210, },
|
||||
{ +18183, +14237, -213, +3919, -3116, -7974, -2292, +2020, -153, +42, -1423, +627, +2562, -2113, -1333, -1554, -1186, -770, -1762, -91, -66, +691, -288, +385, +793, +1048, +304, -885, -866, -584, +1104, +190, },
|
||||
{ +17916, +13240, +200, +4684, -3796, -7718, -2760, +1604, +850, +225, -1927, +1081, +2619, -2191, -1377, -2216, -746, -653, -2263, +733, +68, +56, +169, +482, +600, +1299, +31, -544, -723, -1663, +902, +562, },
|
||||
{ +17044, +12267, +1399, +4803, -4676, -5966, -3841, +281, +3320, -114, -3303, +2279, +2401, -2496, -1283, -2365, -671, -464, -1900, +808, -230, -25, +729, +96, +303, +1800, +66, -835, -309, -1837, -28, +731, },
|
||||
{ +17131, +11616, +1113, +3708, -4510, -4182, -4413, -204, +4141, -354, -3431, +2353, +1909, -2319, -1067, -2887, -24, -22, -2046, +700, -301, +199, +609, -123, +449, +1846, +102, -822, -495, -1668, -259, +595, },
|
||||
{ +17804, +10827, -833, +4161, -4620, -3463, -2984, -1411, +4085, +122, -3689, +2063, +1883, -2404, -1128, -2081, +2, -158, -1877, +784, -113, -307, +389, +403, +575, +1373, +163, -658, -728, -1403, -144, +238, },
|
||||
{ +17770, +9675, -2120, +5437, -4251, -3736, -2292, -788, +3134, -434, -2948, +1807, +1772, -2590, -638, -1520, -124, -311, -1778, +1073, -328, -581, +430, +559, +654, +1248, -5, -604, -701, -1222, +85, +89, },
|
||||
{ +16983, +8744, -2777, +6694, -3792, -4443, -901, -815, +1755, -54, -2575, +1415, +1766, -2182, -536, -1198, -147, -428, -1414, +749, -461, -344, +355, +328, +855, +1312, -290, -626, -512, -1025, +51, +365, },
|
||||
{ +15530, +7980, -1979, +6512, -3528, -3269, -860, -1658, +1879, -8, -2551, +1210, +1950, -1724, -587, -1052, -96, -226, -1441, +427, -306, -235, +212, +345, +877, +1182, -243, -745, -344, -674, -107, +390, },
|
||||
{ +13644, +7839, +340, +4237, -4066, -2089, +181, -157, -98, -716, -1664, +1617, +1169, -1811, +342, -1102, -326, +22, -1305, +431, -226, -614, +436, +265, -325, +880, +370, -95, +206, -791, -194, -105, },
|
||||
{ +11477, +7542, +1372, +3307, -3045, -1070, -418, -498, +213, -531, -1761, +1305, +1366, -1451, +150, -939, -219, -56, -1110, +275, -259, -524, +318, +167, -195, +840, +316, +6, -31, -753, +66, +40, },
|
||||
{ +9185, +7569, +2375, +2356, -1914, -448, -695, -709, +367, -353, -1571, +1152, +1173, -975, +111, -735, -206, -120, -786, +213, -329, -455, +368, +102, -13, +690, +287, +199, -74, -549, +265, +253, },
|
||||
{ +7687, +7172, +2505, +2384, -1188, -623, -434, -481, +255, -398, -1198, +1009, +1022, -762, +89, -484, -279, -47, -579, +42, -268, -226, +195, +11, +172, +581, +417, +303, -47, -184, +406, +81, },
|
||||
{ +6794, +6483, +2410, +2627, -840, -796, -81, -127, -26, -355, -786, +799, +900, -672, +188, -249, -386, -6, -336, -8, -247, -119, +38, +96, +339, +606, +665, +312, -3, +47, +278, -125, },
|
||||
{ +5907, +5826, +2525, +2670, -555, -752, +41, +149, -99, -234, -613, +533, +888, -428, +180, -214, -321, +180, -187, -225, -103, +27, -39, +368, +489, +694, +754, +175, -13, +28, +125, -216, },
|
||||
{ +5068, +5227, +2668, +2678, -280, -715, +86, +472, -115, -212, -504, +385, +890, -299, +204, -116, -199, +274, -206, -202, +165, +190, +121, +464, +488, +765, +530, -95, +33, +85, +32, -285, },
|
||||
{ +4430, +4893, +2607, +2422, +45, -558, +143, +594, -112, -70, -407, +181, +893, +9, +138, -80, -17, +293, -18, +126, +441, +150, +103, +549, +369, +433, +333, -10, +90, +43, -106, -245, },
|
||||
{ +3711, +4345, +2802, +2585, +142, -598, +212, +846, -12, -130, -349, +202, +915, +136, +144, +26, +282, +585, +264, +261, +271, +98, +65, +247, +259, +366, +292, +2, +95, +26, -184, -171, },
|
||||
{ +3270, +4161, +2763, +2345, +192, -419, +378, +857, +49, -39, -256, +254, +934, +196, +366, +592, +573, +461, +232, +248, -23, -159, +76, +241, +206, +248, +207, +95, +139, -106, -191, -197, },
|
||||
{ +2943, +3918, +2802, +2227, +112, -304, +629, +924, +29, +150, -154, +182, +1323, +752, +534, +526, +437, +413, +22, -194, -72, +85, -38, +83, +224, +251, +213, +42, +65, -167, -226, -198, },
|
||||
{ +2772, +3851, +2788, +1975, +65, -69, +845, +1077, -57, +182, +530, +759, +1311, +513, +472, +465, +64, +67, -3, -148, -49, +2, -85, +278, +204, -7, +177, +109, -60, -207, -187, -266, },
|
||||
{ +2741, +3962, +2753, +1658, -41, +420, +934, +1003, +697, +759, +554, +442, +1100, +376, +43, +199, +55, +12, -47, -241, +16, +219, -40, +38, -37, +66, +191, +16, -90, -266, -234, -204, },
|
||||
{ +3100, +4428, +2177, +1205, +75, +902, +1974, +1358, +427, +424, +567, +206, +484, +165, +130, +95, -110, -9, -43, +93, +142, -69, -204, -13, -9, +23, +198, -26, -264, -205, -104, -146, },
|
||||
{ +3210, +4553, +1862, +1097, +1209, +1702, +1662, +1004, +308, +184, +225, +17, +388, +79, +50, +47, -163, +200, +252, -95, -147, -45, -164, -179, -57, +145, +35, -75, -120, -139, -145, -187, },
|
||||
{ +2744, +4295, +2964, +2006, +764, +1468, +1817, +756, +4, +41, +245, +14, +163, +49, +70, +100, +168, +231, -53, -108, -40, -137, -327, -125, -50, -25, +121, +79, -87, -207, -216, -224, },
|
||||
{ +3644, +5506, +2291, +880, +1111, +1504, +1276, +605, -113, -2, +218, +64, +212, -104, +192, +519, +136, -208, +41, +48, -219, -274, -268, -122, -133, +102, +215, +67, -186, -291, -229, -287, },
|
||||
{ +3729, +5991, +1991, +263, +1099, +1310, +1186, +802, +62, -222, +346, +155, +106, +141, +330, +491, +26, -42, +16, -23, -226, -214, -280, -265, -52, +117, +214, +32, -128, -310, -288, -270, },
|
||||
{ +2626, +5352, +3385, +581, +252, +872, +1262, +1271, +175, -237, +438, +384, +61, +61, +469, +546, +232, -19, -21, +71, -83, -296, -233, -189, -195, +42, +177, +99, -95, -207, -255, -295, },
|
||||
{ +2086, +4619, +3769, +1305, +139, +491, +966, +1193, +432, -210, +327, +607, +212, +43, +405, +422, +412, +221, +64, +98, -131, -143, -94, -220, -249, -9, +178, +14, -136, -131, -163, -256, },
|
||||
{ +2139, +4433, +3497, +1555, +347, +189, +878, +1324, +318, -496, +231, +753, +258, +26, +394, +493, +349, +186, +248, +240, -44, -89, -49, -142, -57, -56, +21, +118, -122, -273, -226, -136, },
|
||||
{ +2311, +4528, +3281, +1515, +485, +155, +824, +1324, +265, -492, +160, +527, +142, +7, +422, +391, +265, +431, +202, +86, +149, +36, +38, -14, +14, +37, +95, +88, -115, -263, -276, -244, },
|
||||
{ +2483, +4762, +3179, +1400, +563, +131, +828, +1336, +202, -523, +128, +527, +103, -226, +171, +342, +142, +412, +338, +45, +92, -94, +172, +187, +6, +205, +202, +105, -77, -196, -299, -324, },
|
||||
{ +2726, +5137, +3097, +1180, +674, +184, +755, +1346, +184, -531, +98, +477, +195, -246, -36, +193, -87, +205, +506, +62, -30, -76, +59, +215, +91, +296, +329, +186, +97, -139, -324, -248, },
|
||||
{ +3045, +5527, +3012, +1062, +756, +111, +709, +1423, +99, -641, +145, +477, +146, -204, -123, +100, -177, -119, +444, +91, -147, -88, -72, +196, +100, +177, +408, +293, +113, +30, -141, -297, },
|
||||
{ +3436, +6020, +2910, +889, +868, +57, +655, +1504, +22, -713, +161, +469, +124, -165, -125, -15, -195, -258, +287, +219, -316, -262, -35, +129, +77, +61, +409, +298, +70, +141, -16, -144, },
|
||||
{ +3894, +6586, +2783, +668, +954, -13, +635, +1573, -146, -783, +246, +445, +2, -172, -63, -91, -294, -318, +133, +279, -237, -506, -107, +75, +8, +47, +226, +248, +95, +53, -4, -57, },
|
||||
{ +4405, +7200, +2656, +443, +1039, -128, +662, +1690, -398, -841, +393, +450, -138, -226, +1, -146, -386, -359, +40, +223, -89, -563, -227, +109, -110, -3, +147, +88, +56, +29, -5, -116, },
|
||||
{ +4876, +7873, +2612, +205, +1070, -269, +712, +1849, -697, -944, +597, +475, -291, -315, +82, -190, -515, -429, -4, +168, -34, -473, -351, +121, -57, -127, +140, -117, -82, +81, -67, -139, },
|
||||
{ +5345, +8532, +2640, -20, +1042, -466, +776, +2045, -1057, -1078, +828, +511, -446, -439, +123, -190, -656, -517, -117, +119, +9, -405, -323, -39, +71, -105, -9, -130, -314, -1, -54, -197, },
|
||||
{ +5755, +9172, +2804, -162, +957, -687, +828, +2288, -1359, -1285, +1083, +619, -595, -549, +140, -166, -730, -627, -255, +75, +66, -414, -171, -94, -6, +93, -105, -228, -284, -205, -114, -134, },
|
||||
{ +6053, +9828, +3094, -338, +874, -821, +750, +2423, -1504, -1522, +1217, +797, -715, -693, +145, -172, -790, -680, -456, -66, +242, -494, -151, +117, -189, +150, +39, -395, -358, -190, -293, -70, },
|
||||
{ +6365, +10439, +3396, -513, +841, -814, +596, +2360, -1502, -1631, +1210, +940, -723, -805, +124, -197, -871, -667, -563, -359, +324, -281, -332, +308, -121, -25, +252, -378, -573, -197, -328, -115, },
|
||||
{ +6691, +10976, +3599, -570, +917, -864, +376, +2212, -1463, -1679, +1089, +1036, -703, -886, +72, -213, -1017, -664, -582, -629, +140, +41, -340, +170, +189, -205, +228, -137, -721, -390, -282, -214, },
|
||||
{ +7655, +11301, +2983, -165, +1190, -1185, +265, +2137, -1680, -1486, +973, +1045, -655, -1003, +145, -320, -1056, -774, -554, -718, -142, +148, -1, -9, +263, -7, +1, +12, -602, -637, -348, -173, },
|
||||
{ +8725, +11372, +2035, +795, +1645, -1977, +191, +2335, -2001, -1434, +933, +1003, -589, -1155, +213, -455, -1024, -893, -641, -701, -356, -16, +344, +128, +51, +231, -87, -31, -405, -710, -562, -171, },
|
||||
{ +9575, +11154, +1237, +2394, +2028, -3285, +418, +2886, -2551, -1615, +1186, +934, -650, -1197, +283, -624, -1011, -875, -759, -706, -442, -240, +477, +441, -54, +319, -30, -127, -305, -565, -677, -372, },
|
||||
{ +10190, +10640, +774, +4347, +2002, -4744, +1042, +3458, -3410, -1873, +1669, +748, -934, -1066, +314, -877, -1066, -778, -875, -839, -432, -417, +363, +615, +59, +388, -7, -218, -333, -323, -683, -569, },
|
||||
{ +11656, +10948, -887, +4545, +3052, -4722, +604, +3331, -3530, -1765, +1917, +460, -1170, -858, +331, -1145, -1038, -801, -770, -934, -391, -415, +118, +836, +219, +336, +68, -117, -415, -229, -603, -466, },
|
||||
{ +13296, +11164, -2472, +4349, +3911, -4234, +37, +3058, -3561, -1609, +2035, +238, -1451, -733, +380, -1485, -961, -904, -594, -975, -451, -230, -189, +825, +484, +313, +29, -4, -374, -223, -531, -267, },
|
||||
{ +14547, +11879, -3970, +3666, +4639, -3257, -708, +2677, -3398, -1566, +2096, +56, -1592, -844, +432, -1787, -948, -1000, -471, -858, -654, +26, -371, +599, +668, +477, -138, +127, -242, -320, -341, -194, },
|
||||
{ +16244, +12044, -5188, +3051, +4787, -2203, -1047, +2078, -3288, -1206, +1927, -153, -1529, -1122, +410, -1934, -1051, -951, -400, -689, -749, +91, -214, +211, +759, +692, -292, +255, -104, -432, -82, -239, },
|
||||
{ +17280, +12976, -6370, +2340, +4641, -1173, -1098, +1398, -2972, -1077, +1787, -398, -1249, -1610, +388, -1966, -1197, -775, -431, -518, -766, +52, -3, +3, +536, +1033, -423, +244, +74, -499, +32, -201, },
|
||||
{ +18875, +13104, -7286, +2064, +3976, -486, -755, +780, -2840, -753, +1487, -511, -1136, -1879, +201, -1926, -1197, -654, -442, -429, -635, -89, +257, -23, +237, +1321, -511, +32, +268, -506, +24, +2, },
|
||||
{ +20130, +13417, -7954, +1823, +3146, -160, -302, +339, -2656, -706, +1344, -730, -995, -2122, -28, -1814, -1115, -678, -496, -246, -631, -73, +307, +224, -38, +1338, -479, -375, +351, -455, +133, +122, },
|
||||
{ +20900, +14050, -8284, +1502, +2221, -215, +187, +146, -2582, -775, +1230, -1057, -723, -2335, -281, -1549, -1106, -814, -527, -46, -662, -26, +311, +623, -314, +1032, -275, -849, +139, -141, +262, +150, },
|
||||
{ +21506, +14520, -8163, +1322, +1076, -434, +702, +70, -2585, -746, +944, -1321, -256, -2471, -490, -1259, -1129, -1001, -507, +204, -799, +188, +407, +732, -431, +631, -175, -1085, -90, -20, +511, +450, },
|
||||
{ +22045, +14638, -7551, +1339, -401, -605, +1069, +20, -2608, -672, +522, -1429, +425, -2597, -667, -990, -1249, -1112, -381, +216, -660, +423, +180, +808, -430, +149, -101, -901, -420, -144, +988, +646, },
|
||||
{ +22374, +14605, -6584, +1379, -2012, -818, +1269, -52, -2715, -579, +90, -1306, +1138, -2771, -762, -975, -1284, -1081, -478, +502, -689, +317, +67, +770, -529, -3, +37, -932, -233, -321, +645, +1063, },
|
||||
{ +21971, +14982, -5381, +1465, -3401, -1307, +1361, -157, -2826, -434, -42, -1263, +1888, -2842, -1101, -790, -1377, -899, -394, +320, -814, +398, -273, +728, -246, -176, +85, -530, -151, -691, +222, +1349, },
|
||||
{ +21578, +15032, -3953, +1480, -4478, -2119, +1347, -259, -2912, +19, -196, -1128, +2559, -2963, -1432, -703, -946, -870, -888, +371, -914, +216, -287, +839, -198, -30, +401, -611, -215, -354, -239, +533, },
|
||||
{ +20935, +14892, -2350, +1522, -5169, -3135, +939, +47, -2856, +331, -223, -886, +3031, -3198, -1627, +82, -1303, -1105, -934, +12, -879, +350, -351, +790, +317, +101, -59, -207, +109, -776, -140, -183, },
|
||||
{ +19989, +14916, -1024, +1608, -5475, -4473, +295, +1065, -2834, +220, +84, -593, +2619, -2455, -1492, -396, -1160, -1373, -1255, +21, -642, +54, -86, +1220, -111, +160, +248, -322, +230, -981, +131, -465, },
|
||||
{ +19442, +14440, -244, +2209, -5944, -5867, +441, +1708, -2969, +837, -323, -569, +3451, -2723, -1540, -344, -1757, -1134, -1142, -251, -519, +531, -17, +686, +62, +600, -73, -568, +824, -1013, -326, +77, },
|
||||
{ +19215, +13282, +497, +2823, -6861, -6114, +314, +2076, -2340, +432, -449, +228, +3074, -2653, -1482, -1155, -1421, -644, -1745, -8, +375, -76, -205, +1100, -38, +316, +113, -497, +839, -778, -681, +216, },
|
||||
{ +18399, +12398, +1273, +2931, -6997, -6116, +179, +2120, -1724, +857, -1704, +1041, +3221, -3315, -1375, -1169, -1113, -795, -1352, +352, -102, -102, +206, +701, -238, +511, +174, -405, +646, -548, -516, -399, },
|
||||
{ +17103, +12076, +1628, +2442, -6317, -5062, -1021, +2253, +17, -617, -1910, +2032, +2182, -3218, -758, -1593, -918, +124, -1676, +57, +262, -74, -107, +549, -37, +456, +200, -287, +594, -479, -420, -621, },
|
||||
{ +17339, +11281, -244, +2822, -5940, -4177, +36, +1140, -56, -183, -2196, +2015, +1763, -3155, -461, -1392, -347, -94, -1944, +612, +306, -660, +4, +659, -17, +432, +20, -3, +720, -783, -494, -283, },
|
||||
{ +16567, +9681, +78, +3330, -6088, -2392, -403, +490, +834, -1104, -2339, +2535, +1400, -3259, +240, -1123, -378, -183, -1760, +840, -40, -771, +203, +636, -208, +524, +221, -45, +609, -787, -453, -228, },
|
||||
{ +15277, +8488, +278, +3885, -5037, -2468, -191, +698, +82, -1238, -1833, +2336, +911, -2516, +590, -1228, -341, -135, -1481, +650, -204, -726, +413, +395, -341, +823, +281, -147, +494, -733, -476, -140, },
|
||||
{ +14508, +10388, +2390, +614, -9568, -3171, +2824, +3138, -360, -1806, -3125, +365, +1587, -1832, +1166, -336, -464, +624, -617, +527, +1, -567, -177, -592, -325, +573, -499, -34, +977, -258, +261, -46, },
|
||||
{ +12204, +10145, +3137, +1381, -8141, -4350, +1999, +2595, +500, -763, -2949, -365, +1128, -983, +987, -357, -350, +719, -509, +347, +303, -394, -148, -488, -492, +295, -634, +121, +1110, -75, +436, -20, },
|
||||
{ +10618, +9407, +3284, +1743, -6720, -3899, +1478, +1813, +596, -356, -2413, -640, +1096, -622, +604, -220, -317, +679, -315, +187, +316, -151, -180, -467, -394, +122, -486, +261, +994, +154, +417, -67, },
|
||||
{ +8737, +8566, +3830, +1870, -5294, -3295, +668, +1407, +736, -98, -1874, -761, +1017, -457, +347, -65, -252, +598, -129, +108, +374, +35, -238, -389, -303, +101, -170, +351, +816, +261, +307, -90, },
|
||||
{ +7322, +7990, +4204, +1759, -4233, -2859, +204, +1225, +706, +89, -1428, -779, +812, -371, +310, +13, -255, +576, +48, +68, +394, +93, -230, -185, -105, +97, -62, +325, +596, +234, +208, -140, },
|
||||
{ +5934, +7313, +4453, +1864, -3250, -2602, -73, +1073, +737, +266, -1100, -695, +649, -339, +267, +163, -183, +531, +195, +93, +388, +271, +42, +55, -87, -115, -13, +235, +333, +198, +167, -56, },
|
||||
{ +5000, +6676, +4573, +2086, -2672, -2459, -188, +999, +803, +304, -865, -518, +450, -355, +364, +251, -120, +548, +192, +279, +680, +344, +32, +73, -196, -341, +1, +192, +208, +250, +135, -143, },
|
||||
{ +4456, +6275, +4433, +1984, -2322, -2108, -127, +872, +815, +394, -642, -454, +355, -269, +459, +303, -78, +714, +511, +458, +618, +237, -33, -56, -404, -289, +129, +56, +240, +141, -2, +127, },
|
||||
{ +3914, +5714, +4351, +2080, -2013, -1928, -97, +902, +872, +413, -420, -323, +306, -177, +447, +601, +422, +769, +448, +347, +442, +17, -200, -32, -412, -202, +177, -120, +89, +302, +138, +79, },
|
||||
{ +3687, +5529, +4104, +1830, -1878, -1601, +73, +886, +904, +439, -190, -238, +213, +104, +832, +826, +315, +493, +327, +220, +233, -30, -126, -120, -318, -150, -96, +60, +301, +96, +30, +144, },
|
||||
{ +3580, +5372, +3781, +1521, -1776, -1190, +327, +859, +987, +532, -67, +9, +629, +331, +636, +568, +95, +278, +193, +191, +184, -23, -203, -172, -324, -16, +176, -130, +89, +51, +115, +194, },
|
||||
{ +3492, +5329, +3471, +1350, -1561, -940, +597, +949, +959, +721, +441, +128, +352, +175, +389, +425, +9, +204, +280, +156, +63, -22, -262, -158, +108, -5, -216, -107, +108, +31, +78, +239, },
|
||||
{ +3432, +5245, +3183, +1194, -1128, -596, +840, +1170, +1253, +888, +199, -114, +53, +17, +302, +263, +132, +181, +140, +117, +66, -50, -59, +236, -176, -301, -113, -66, -72, -13, +269, +19, },
|
||||
{ +3390, +5248, +2963, +1119, -596, -247, +1125, +1471, +935, +484, +120, -201, +1, -69, +296, +276, +64, +116, +141, +246, -26, +224, +239, -232, -240, -143, -165, -208, -22, +53, +4, +96, },
|
||||
{ +3459, +5308, +2829, +1096, -226, +235, +1196, +1070, +638, +314, +33, -149, +26, -184, +259, +284, -110, +166, +359, +85, +180, +500, -207, -255, -125, -253, -294, -112, -3, -243, +82, +267, },
|
||||
{ +3444, +5608, +3052, +1048, -153, +152, +952, +951, +500, +280, +174, -297, -12, -51, +82, +247, +11, +278, +212, +303, +571, -23, -220, +24, -291, -435, -178, -82, -257, -114, +197, +358, },
|
||||
{ +3631, +5987, +3171, +840, -377, -261, +737, +1132, +564, +264, +192, -346, -126, +1, +155, +186, +152, +235, +119, +688, +415, -209, -47, -46, -307, -433, -160, -284, -252, +134, +257, +168, },
|
||||
{ +3555, +6368, +3416, +601, -552, -585, +576, +1098, +540, +380, +255, -412, -120, +84, +90, +257, +276, +51, +139, +958, +303, -229, +70, -105, -327, -291, -183, -450, -241, +244, +389, -20, },
|
||||
{ +3304, +6445, +3877, +542, -780, -691, +604, +1149, +403, +125, +220, -232, -86, +58, +247, +261, +126, +140, +91, +859, +535, -186, +15, -49, -295, -232, -192, -427, -225, +175, +356, +33, },
|
||||
{ +3095, +6398, +4320, +718, -1060, -852, +617, +1319, +551, -131, -94, -318, -227, +167, +484, +239, +130, +167, -116, +734, +786, -134, -76, +64, -201, -204, -176, -362, -225, +158, +343, -8, },
|
||||
{ +3043, +6443, +4566, +841, -1213, -1017, +635, +1471, +644, -187, -192, -362, -461, -72, +479, +445, +148, +181, -13, +421, +845, +87, -217, -18, -79, -55, -112, -366, -227, +211, +309, +49, },
|
||||
{ +3132, +6650, +4683, +897, -1394, -1203, +748, +1560, +723, -251, -284, -272, -531, -243, +278, +347, +138, +203, +193, +288, +760, +331, -284, -258, -57, +43, +11, -256, -228, +216, +280, +136, },
|
||||
{ +3367, +6988, +4849, +842, -1674, -1375, +807, +1795, +719, -420, -285, -270, -512, -280, +138, +254, -47, -57, +304, +307, +642, +594, -125, -511, -291, +9, +125, -134, -237, +289, +287, +139, },
|
||||
{ +3586, +7407, +5080, +656, -1907, -1495, +823, +2057, +683, -633, -291, -228, -494, -258, +64, +194, -36, -286, +173, +318, +479, +623, +210, -460, -540, -128, +140, -101, -240, +315, +252, +163, },
|
||||
{ +3913, +8036, +5294, +392, -2186, -1751, +902, +2457, +567, -915, -352, -187, -458, -275, +94, +106, -67, -364, +29, +257, +307, +466, +372, -273, -572, -259, +20, -46, -279, +200, +193, +90, },
|
||||
{ +4032, +8688, +5666, +129, -2445, -1998, +927, +2764, +585, -1205, -445, -136, -465, -297, +145, +30, -86, -398, -75, +234, +196, +315, +365, -111, -602, -339, +4, +2, -305, +120, +111, -149, },
|
||||
{ +4592, +9563, +5846, -404, -2900, -2105, +1130, +3189, +362, -1558, -421, -109, -448, -336, +218, +21, -218, -346, -123, +178, +175, +130, +357, -19, -545, -388, -95, -2, -115, +87, -12, -264, },
|
||||
{ +5108, +10372, +5960, -878, -3340, -2212, +1378, +3487, +158, -1928, -397, -33, -497, -372, +269, +15, -353, -346, -123, +153, +121, -36, +345, +17, -453, -394, -151, -80, -129, +237, -86, -306, },
|
||||
{ +5903, +11205, +5822, -1373, -3825, -2282, +1849, +3676, -196, -2204, -349, +96, -588, -398, +334, +40, -480, -394, -80, +155, +103, -202, +283, +76, -376, -305, -191, -130, -153, +273, -64, -333, },
|
||||
{ +6463, +12077, +6105, -1736, -4427, -2497, +2172, +3937, -457, -2637, -316, +269, -650, -515, +328, +128, -574, -464, -145, +165, +112, -359, +102, +133, -284, -189, -158, -224, -158, +175, +22, -412, },
|
||||
{ +6842, +12772, +6370, -1805, -4912, -2802, +2482, +4054, -577, -3009, -381, +505, -711, -614, +320, +143, -632, -456, -212, +124, +133, -429, -89, +129, -159, -163, +4, -302, -109, +98, -9, -351, },
|
||||
{ +7495, +13436, +6402, -1641, -5389, -3222, +2902, +4096, -819, -3320, -508, +760, -709, -801, +367, +144, -733, -443, -242, +90, +109, -405, -353, -3, +96, -160, +135, -243, -147, +159, -109, -342, },
|
||||
{ +8302, +13982, +6007, -1545, -5551, -3449, +3272, +3931, -1108, -3384, -659, +902, -637, -984, +381, +171, -829, -414, -266, +67, +40, -321, -455, -318, +312, -35, +128, -72, -215, +189, -55, -445, },
|
||||
{ +9896, +14670, +4570, -1730, -5262, -3249, +3511, +3414, -1572, -3150, -826, +851, -414, -1191, +310, +159, -852, -430, -228, +45, -97, -288, -414, -643, +337, +260, +1, +181, -141, +21, +120, -543, },
|
||||
{ +11175, +14744, +3448, -993, -4976, -3811, +3696, +3441, -2038, -3160, -852, +834, -325, -1343, +248, +102, -877, -401, -118, -49, -199, -292, -342, -788, +115, +605, -66, +267, +58, -136, +206, -540, },
|
||||
{ +12433, +14644, +2289, -11, -4664, -4428, +3847, +3503, -2546, -3196, -721, +719, -321, -1413, +201, -27, -944, -298, +67, -214, -293, -242, -375, -807, -141, +822, +24, +219, +276, -175, +133, -469, },
|
||||
{ +14253, +15077, +91, +194, -3679, -4227, +3554, +3173, -2873, -3182, -505, +510, -236, -1531, +106, -141, -941, -242, +293, -223, -457, -200, -494, -638, -319, +772, +318, +229, +335, -59, -21, -432, },
|
||||
{ +16147, +15220, -1962, +114, -2901, -3609, +3134, +2739, -3044, -3270, -314, +358, -218, -1704, +85, -332, -895, -157, +414, -203, -648, -134, -650, -486, -341, +596, +483, +347, +229, -1, -55, -568, },
|
||||
{ +17862, +15961, -4207, -422, -1951, -2655, +2484, +2264, -2914, -3618, -117, +208, -161, -1924, +25, -473, -800, -35, +478, -79, -937, -17, -769, -367, -225, +425, +413, +582, +113, -133, +87, -734, },
|
||||
{ +19449, +16392, -5859, -1112, -1427, -1629, +1942, +1918, -2765, -3978, -8, +84, -134, -2062, -13, -566, -654, +37, +477, +32, -1157, +83, -816, -331, +13, +340, +96, +768, +67, -341, +213, -694, },
|
||||
{ +21050, +17015, -7348, -2198, -1140, -508, +1499, +1662, -2610, -4451, +6, -138, -34, -2121, -29, -596, -503, +48, +407, +189, -1351, +208, -923, -200, +286, +156, -272, +696, +143, -514, +278, -464, },
|
||||
{ +22180, +17691, -8318, -3153, -1264, +222, +1444, +1546, -2552, -4822, -136, -360, +172, -2054, -32, -523, -483, -7, +341, +375, -1419, +217, -913, -27, +273, +51, -545, +431, +314, -476, +252, -284, },
|
||||
{ +23145, +18305, -8899, -3912, -1872, +754, +1676, +1457, -2542, -5140, -436, -571, +593, -1943, -18, -391, -590, -105, +360, +600, -1486, +265, -834, -64, +157, -1, -808, +207, +535, -343, +188, -148, },
|
||||
{ +24201, +18791, -9169, -4533, -3193, +1197, +2201, +1353, -2588, -5477, -898, -646, +1316, -1907, +112, -361, -830, -56, +459, +703, -1301, +324, -1128, -106, +17, -169, -828, +111, +625, -123, +274, -146, },
|
||||
{ +25039, +18854, -8960, -4922, -4675, +1488, +2597, +1309, -2785, -5629, -1226, -537, +1894, -1872, +177, -569, -818, +47, +414, +930, -1141, -66, -1306, -147, -197, -117, -643, -26, +588, +205, +269, -377, },
|
||||
{ +25483, +18997, -8159, -5303, -6464, +1415, +3101, +1247, -3048, -5328, -1549, -448, +2627, -1908, -26, -525, -721, +144, +628, +830, -1334, -263, -1576, -203, -94, -5, -521, -41, +604, +221, +328, -313, },
|
||||
{ +25310, +19394, -7240, -5364, -8082, +861, +3602, +1021, -2853, -4841, -1925, -274, +3034, -1996, -184, -315, -585, +480, +190, +564, -1199, -604, -1602, -1, -64, +5, -267, -39, +307, +300, +702, -524, },
|
||||
{ +25187, +19126, -5747, -5306, -10048, +5, +3816, +1259, -2439, -4328, -2144, -285, +3285, -2132, -193, +313, -577, +11, -100, +308, -1123, -638, -1463, +80, -40, +174, -302, -152, +368, +273, +817, -512, },
|
||||
{ +24584, +19059, -4370, -5103, -11540, -1206, +4149, +1948, -2239, -3792, -2260, -463, +3232, -1933, +310, +264, -1049, -39, -461, +305, -807, -683, -1310, +216, -69, -19, -134, -7, +199, +192, +1012, -495, },
|
||||
{ +24229, +18498, -3219, -4673, -13052, -2239, +4802, +2529, -2152, -3029, -2527, -945, +3759, -1608, +81, +180, -1445, -83, -369, +279, -585, -589, -996, -15, -262, +176, +17, -164, +214, +234, +1010, -493, },
|
||||
{ +23479, +17761, -2169, -4054, -14542, -2764, +5166, +3024, -1593, -2674, -3100, -489, +3855, -1932, +199, -475, -1408, +376, -646, +385, -160, -507, -1201, -125, -73, +89, +2, -134, +277, +184, +889, -456, },
|
||||
{ +22902, +16611, -1241, -3921, -14659, -3126, +5151, +3820, -1738, -2209, -3026, -680, +3843, -2076, -205, -376, -1050, +290, -561, +675, -171, -734, -967, -54, -279, +107, +127, -143, +289, +105, +906, -440, },
|
||||
{ +21520, +15491, -466, -3170, -14169, -3623, +5060, +3934, -933, -1908, -3770, -160, +3317, -2389, +45, -293, -930, +570, -302, +294, -117, -531, -816, -259, -393, +309, +177, -338, +355, +325, +623, -455, },
|
||||
{ +20196, +14258, -26, -2087, -13378, -3959, +4522, +4579, -753, -2187, -3650, -282, +3062, -2403, +220, -314, -596, +900, -834, +333, +193, -640, -766, -353, -377, +474, -67, -401, +722, +187, +352, -286, },
|
||||
{ +18562, +12495, +606, -395, -12713, -4067, +4718, +3983, -631, -1865, -4106, +267, +2676, -2530, +544, -90, -270, +517, -885, +629, +178, -816, -539, -358, -375, +420, -176, -225, +814, +58, +205, -176, },
|
||||
{ +16507, +11335, +1670, +425, -11680, -3415, +3753, +3482, -140, -2175, -3676, +452, +2142, -2340, +899, +31, -525, +540, -719, +620, +74, -768, -288, -458, -444, +527, -237, -258, +969, -83, +155, -100, },
|
||||
{ +15749, +11088, +540, -1465, -10587, +5, +4768, +1482, -1271, -1294, -2408, -204, +2109, -2180, +700, +142, -524, +394, -343, +524, -78, -510, -510, -119, -176, +83, +45, -8, -121, -132, +846, -57, },
|
||||
{ +12780, +11168, +3101, -713, -10375, -2419, +3165, +2486, +741, -926, -2637, -901, +1193, -1380, +1171, +201, -695, +683, -287, +203, +326, -265, -402, +216, -760, -316, +56, -240, +385, +349, +800, +38, },
|
||||
{ +10481, +10174, +3731, +12, -8189, -2627, +2287, +2162, +639, -620, -1980, -947, +827, -877, +947, +162, -591, +622, -179, +124, +414, -151, -214, +218, -687, -257, +155, -179, +273, +413, +653, +58, },
|
||||
{ +8668, +9236, +4001, +664, -6447, -2669, +1688, +1709, +604, -245, -1509, -943, +653, -570, +711, +170, -456, +560, -83, +116, +460, +30, -144, +259, -390, -222, +102, -244, +172, +370, +519, +108, },
|
||||
{ +7283, +8384, +4111, +1067, -5156, -2491, +1257, +1309, +661, +29, -1138, -850, +514, -376, +522, +237, -336, +496, +41, +153, +499, +232, +59, +319, -295, -329, -17, -275, -28, +319, +573, +102, },
|
||||
{ +6272, +7691, +4060, +1222, -4187, -2162, +939, +1051, +723, +183, -806, -720, +436, -270, +454, +290, -208, +496, +100, +362, +696, +335, +52, +222, -281, -423, -148, -305, -83, +332, +591, +108, },
|
||||
{ +5580, +7158, +3875, +1174, -3460, -1834, +755, +904, +761, +294, -524, -552, +331, -184, +477, +333, -134, +601, +379, +449, +583, +227, -20, +147, -340, -469, -157, -333, -172, +385, +542, +14, },
|
||||
{ +5099, +6769, +3611, +1012, -2865, -1522, +671, +853, +823, +385, -298, -392, +284, -75, +496, +488, +66, +592, +366, +331, +409, +167, -75, +79, -239, -485, -266, -327, -145, +317, +622, +200, },
|
||||
{ +4774, +6505, +3318, +801, -2367, -1254, +708, +915, +878, +465, -124, -221, +265, +118, +634, +457, -4, +374, +332, +279, +241, +173, -17, +50, -250, -474, -271, -392, +13, +518, +389, +79, },
|
||||
{ +4533, +6373, +3052, +547, -1931, -979, +831, +1054, +874, +581, -34, -52, +431, +69, +479, +349, -93, +183, +314, +308, +170, +203, +2, -44, -122, -409, -440, -16, +135, +84, +235, +185, },
|
||||
{ +4382, +6335, +2815, +384, -1572, -712, +951, +1234, +846, +634, +201, -204, +344, +11, +311, +320, -174, +46, +261, +434, +123, +92, +101, +62, -245, -261, -49, -321, -116, +108, +125, +189, },
|
||||
{ +4310, +6316, +2697, +377, -1265, -560, +1000, +1283, +976, +592, +177, -342, +94, +124, +234, +277, -211, -42, +322, +339, +136, +155, +187, -53, -7, +128, -468, -481, -77, +89, +78, +112, },
|
||||
{ +4220, +6370, +2803, +455, -1043, -593, +884, +1257, +898, +647, +131, -407, +17, -24, +259, +268, -143, -71, +248, +315, +54, +318, +56, +134, +452, -358, -580, -280, -245, +17, +207, +32, },
|
||||
{ +4238, +6578, +2947, +418, -979, -561, +705, +1063, +778, +509, +327, -311, -141, -2, +117, +252, +15, +34, +169, +147, +205, +145, +62, +724, +153, -621, -342, -314, -253, +63, +216, -198, },
|
||||
{ +4312, +6878, +3119, +203, -1024, -566, +664, +1052, +477, +317, +296, -326, +41, -20, +78, +256, -64, +129, +238, +109, +104, +11, +281, +812, -119, -527, -289, -348, -126, +151, +43, -331, },
|
||||
{ +4243, +7275, +3432, -18, -1154, -711, +722, +1244, +373, +111, +88, -498, -48, +145, +283, +267, -58, +72, +167, +174, +186, -87, +222, +896, -239, -530, -150, -376, +16, +248, -104, -319, },
|
||||
{ +4240, +7629, +3820, -212, -1418, -875, +841, +1446, +395, +35, -59, -651, -337, +57, +348, +394, +138, +74, +125, +94, +212, -51, +148, +896, -296, -557, -109, -348, +139, +310, -89, -337, },
|
||||
{ +4267, +8079, +4161, -358, -1708, -1137, +1013, +1624, +419, -13, -148, -667, -486, -153, +260, +306, +109, +213, +291, +100, +122, +47, -84, +791, +19, -750, -253, -252, +190, +325, -24, -265, },
|
||||
{ +4428, +8579, +4533, -649, -2161, -1157, +1152, +1818, +471, -285, -99, -625, -629, -167, +110, +208, -67, +58, +397, +149, +182, +241, -312, +521, +338, -764, -423, -220, +99, +352, +41, -277, },
|
||||
{ +4663, +9147, +5017, -957, -2713, -1191, +1270, +2062, +456, -561, -64, -490, -728, -151, +48, +95, -93, -149, +333, +236, +4, +377, -80, +107, +501, -359, -693, -415, +113, +346, +3, -240, },
|
||||
{ +4794, +9798, +5715, -1328, -3293, -1331, +1372, +2391, +376, -861, -165, -318, -751, -218, +113, -43, -176, -257, +208, +326, -101, +123, +104, -60, +376, +187, -610, -661, -80, +210, +60, -231, },
|
||||
{ +4706, +10404, +6857, -1587, -4043, -1501, +1416, +2768, +428, -1274, -318, -83, -773, -321, +190, -68, -276, -333, +89, +301, +70, -126, -66, -7, +48, +426, +52, -740, -438, +212, -62, -314, },
|
||||
{ +5492, +11533, +6802, -2410, -4639, -1493, +1942, +3028, +51, -1662, -327, +15, -748, -355, +194, -55, -404, -364, +59, +221, +123, -131, -285, -124, -44, +334, +429, -330, -642, -6, -1, -451, },
|
||||
{ +5983, +12434, +7309, -2945, -5475, -1428, +2301, +3203, -66, -2148, -439, +316, -869, -393, +284, -166, -434, -371, -45, +257, +65, -121, -270, -467, -42, +274, +467, +199, -506, -380, +16, -415, },
|
||||
{ +6553, +13345, +7759, -3488, -6279, -1393, +2735, +3371, -353, -2547, -584, +610, -877, -578, +354, -136, -577, -381, +10, +115, +137, -230, -229, -570, -338, +370, +460, +362, -28, -508, -363, -242, },
|
||||
{ +7471, +14327, +7633, -4021, -6946, -1337, +3324, +3391, -814, -2875, -657, +809, -860, -751, +399, -191, -586, -460, +66, +94, +17, -214, -308, -558, -507, +258, +589, +343, +231, -149, -748, -423, },
|
||||
{ +8279, +15118, +7553, -4198, -7540, -1469, +3893, +3301, -1178, -3117, -875, +1005, -790, -1000, +413, -197, -660, -436, +72, +94, -104, -258, -361, -556, -505, +12, +646, +436, +181, +263, -660, -907, },
|
||||
{ +9124, +15874, +7171, -4175, -7769, -1782, +4384, +3236, -1562, -3240, -1066, +1125, -708, -1207, +337, -154, -693, -392, +105, +118, -192, -387, -322, -628, -341, -94, +398, +659, +143, +329, -199, -1184, },
|
||||
{ +10600, +16582, +5950, -4368, -7541, -1867, +4737, +3086, -2067, -3335, -1044, +1123, -740, -1306, +237, -176, -627, -343, +172, +164, -330, -475, -330, -675, -210, +76, +40, +658, +284, +168, +174, -1087, },
|
||||
{ +12376, +17175, +4171, -4522, -7036, -1733, +4785, +2866, -2442, -3529, -868, +972, -750, -1432, +142, -233, -536, -280, +203, +260, -566, -441, -421, -666, -125, +321, -234, +424, +421, +32, +318, -810, },
|
||||
{ +14090, +17881, +2149, -4729, -6335, -1510, +4556, +2689, -2622, -3826, -676, +683, -672, -1544, +43, -207, -493, -179, +161, +384, -803, -320, -525, -640, -11, +437, -358, +48, +480, -16, +337, -499, },
|
||||
{ +16479, +18100, -251, -5038, -5617, -895, +4158, +2494, -2872, -4087, -506, +369, -589, -1544, +53, -264, -351, -200, +196, +385, -925, -184, -637, -545, +96, +400, -519, -169, +313, +46, +383, -236, },
|
||||
{ +18719, +18354, -2535, -5382, -5077, -235, +3760, +2374, -3095, -4427, -427, +22, -354, -1411, +7, -243, -275, -312, +263, +406, -941, -99, -688, -428, +90, +202, -566, -359, +122, +172, +472, -78, },
|
||||
{ +20632, +18711, -4603, -5686, -4881, +473, +3484, +2242, -3308, -4836, -393, -315, +167, -1349, -10, -178, -419, -353, +354, +511, -957, -1, -751, -448, -19, +72, -604, -458, +57, +275, +400, +168, },
|
||||
{ +22360, +19050, -6299, -6014, -5124, +1226, +3414, +2007, -3540, -5278, -311, -473, +850, -1407, +137, -306, -606, -202, +424, +682, -953, +71, -1092, -494, -36, -56, -597, -263, -49, +181, +480, +340, },
|
||||
{ +23853, +19254, -7494, -6434, -5724, +1855, +3501, +1649, -3877, -5514, -94, -559, +1541, -1479, +150, -510, -585, -3, +419, +893, -1016, -259, -1392, -389, -165, +23, -418, -178, -252, +244, +451, +239, },
|
||||
{ +25329, +18892, -8176, -6707, -6565, +2362, +3636, +1174, -4256, -5023, -220, -362, +2101, -1684, +93, -508, -345, +44, +530, +734, -1282, -474, -1490, -397, +61, +190, -464, -83, -222, +71, +299, +421, },
|
||||
{ +26132, +18615, -8557, -6606, -7720, +2662, +3828, +564, -4070, -4493, -377, -142, +2382, -2009, +159, -193, -269, +57, +124, +489, -1273, -596, -1558, -23, +257, +40, -292, +5, -420, -52, +361, +377, },
|
||||
{ +26097, +18760, -8279, -6563, -9338, +2395, +4427, +354, -3683, -4052, -417, -411, +2686, -2127, +394, +208, -612, -267, -183, +547, -1260, -477, -1400, +110, +235, +53, -183, -98, -337, -206, +404, +296, },
|
||||
{ +26298, +17883, -7539, -6429, -11135, +2349, +5208, +297, -3531, -3208, -932, -479, +3059, -2027, +586, +52, -1210, -243, -201, +410, -847, -361, -1398, +182, +205, +3, -91, -95, -338, -279, +476, +239, },
|
||||
{ +25890, +16998, -6703, -6158, -12691, +2616, +5823, +131, -2985, -2746, -1498, -267, +3567, -2288, +598, -395, -1379, +90, -420, +614, -490, -504, -1389, +217, +123, -23, -40, -116, -280, -344, +578, +55, },
|
||||
{ +24947, +16037, -5788, -5689, -13461, +2608, +6096, +412, -2813, -2278, -1596, -192, +3523, -2540, +252, -341, -1212, +134, -205, +703, -394, -619, -1225, +253, -81, +26, +22, -90, -211, -394, +628, +37, },
|
||||
{ +23244, +14939, -4628, -4799, -13452, +2196, +6207, +481, -2338, -1733, -1999, -166, +3296, -2924, +380, -214, -1290, +655, -260, +536, -282, -573, -1003, -3, -66, +82, +42, -31, -237, -351, +712, -93, },
|
||||
{ +21175, +13481, -2942, -3774, -12857, +1570, +5657, +1162, -1877, -1714, -2063, -297, +2743, -2620, +466, -249, -749, +711, -463, +451, -19, -579, -1025, +20, -106, +79, +79, -61, -143, -304, +645, -60, },
|
||||
{ +18636, +12183, -1209, -2590, -11846, +582, +5371, +1609, -1826, -1340, -2309, -463, +2623, -2468, +485, +42, -543, +537, -540, +593, +20, -722, -705, -39, -175, +121, +49, +1, -116, -325, +749, +25, },
|
||||
{ +14783, +9489, -2982, -1023, -3993, +3280, +1929, -1888, -1700, +251, +285, -164, +1356, -1971, +151, -32, -982, +252, -342, +144, -27, -417, -521, +840, -3, +115, +200, -545, +44, -370, -154, +120, },
|
||||
{ +12635, +9338, -1709, -1092, -3762, +2615, +2298, -1249, -1537, +64, +39, -164, +1209, -1626, +74, +120, -732, +178, -175, +161, -27, -373, -563, +683, +104, +109, +334, -384, -11, -279, -152, +39, },
|
||||
{ +10824, +8969, -613, -1061, -3373, +2063, +2390, -782, -1282, -126, -128, -73, +1005, -1268, +42, +226, -527, +136, -29, +165, -13, -304, -520, +542, +230, +129, +323, -272, -56, -204, -196, -8, },
|
||||
{ +9429, +8572, +94, -1038, -2958, +1688, +2332, -399, -1026, -257, -166, -8, +812, -970, +100, +281, -355, +129, +93, +200, -9, -181, -418, +422, +262, +108, +261, -198, -85, -190, -163, -35, },
|
||||
{ +8397, +8228, +487, -1075, -2536, +1429, +2193, -104, -809, -286, -144, +0, +671, -693, +170, +287, -214, +140, +163, +259, +27, -77, -357, +258, +245, +93, +144, -149, -82, -138, -162, -157, },
|
||||
{ +7612, +7949, +682, -1088, -2127, +1227, +2044, +150, -627, -240, -104, +9, +638, -490, +230, +309, -107, +161, +239, +304, +6, -19, -321, +91, +206, +63, +62, -59, -63, -155, -170, -131, },
|
||||
{ +6952, +7827, +766, -1099, -1787, +1048, +1937, +367, -509, -160, +31, -46, +675, -344, +225, +366, -39, +163, +218, +347, -43, -35, -270, -73, +151, +125, +10, -31, -65, -114, -144, -245, },
|
||||
{ +6683, +7602, +752, -1154, -1469, +1022, +1800, +518, -463, -59, +237, -126, +665, -182, +167, +400, +1, +65, +208, +350, -66, -128, -244, -86, +66, +169, -26, -43, +21, -177, -95, +91, },
|
||||
{ +6328, +7603, +798, -1168, -1193, +1003, +1709, +558, -362, -97, +468, -68, +448, +37, +143, +230, +82, +36, +95, +341, -19, -201, -272, +32, -38, +101, +81, -48, -56, +78, +109, -161, },
|
||||
{ +6163, +7555, +882, -1153, -951, +1060, +1636, +512, -380, -40, +469, +81, +403, -73, +284, +126, -61, +146, +60, +209, +16, -67, -335, -42, +24, +86, +40, -88, +270, +181, -136, -94, },
|
||||
{ +6061, +7646, +945, -1152, -751, +1127, +1592, +462, -481, -102, +513, +8, +479, -8, +120, +232, -121, -17, +188, +182, -85, +28, -226, -217, -8, +203, -144, +134, +478, -84, -37, -109, },
|
||||
{ +6159, +7784, +897, -1252, -582, +1290, +1597, +367, -567, -244, +423, +7, +359, +71, +248, +105, -133, +44, +12, +207, +16, -67, -239, -155, +10, -42, -17, +448, +145, -81, +130, -268, },
|
||||
{ +6295, +8059, +874, -1507, -476, +1456, +1680, +362, -767, -349, +394, -179, +254, +82, +233, +237, -141, -70, +113, +103, -91, +65, -321, -165, +80, -154, +82, +449, -28, -5, +49, -241, },
|
||||
{ +6464, +8458, +884, -1785, -437, +1608, +1780, +391, -878, -494, +409, -232, +50, -6, +273, +208, -116, +68, +65, +99, -84, -65, -354, -36, +94, -209, +217, +427, -208, -3, +52, -229, },
|
||||
{ +6683, +8970, +878, -2165, -376, +1753, +1869, +420, -1047, -513, +453, -325, -68, -78, +177, +166, -163, +34, +201, +156, -170, -62, -410, -135, +213, -121, +156, +486, -231, -110, -45, -202, },
|
||||
{ +7075, +9466, +776, -2530, -318, +1953, +1897, +349, -1235, -528, +610, -389, -209, -190, +180, +87, -332, -2, +206, +167, -129, -48, -464, -135, +204, -153, +111, +606, -167, -304, -61, -230, },
|
||||
{ +7586, +10041, +588, -2915, -206, +2202, +1934, +234, -1428, -528, +819, -402, -344, -230, +142, +32, -392, -123, +176, +151, -193, +34, -366, -138, +241, -59, -155, +594, +203, -426, -180, -174, },
|
||||
{ +8284, +10637, +233, -3349, -82, +2488, +1888, +4, -1707, -479, +1011, -436, -518, -321, +180, -80, -546, -130, +97, -3, -257, -57, -259, -35, +170, +42, -299, +295, +418, -294, -381, -148, },
|
||||
{ +9095, +11203, -132, -3724, +50, +2818, +1819, -242, -2019, -371, +1236, -443, -675, -423, +213, -107, -683, -188, +152, -197, -308, -122, -264, +206, +142, +61, -245, -25, +422, +25, -540, -195, },
|
||||
{ +9889, +11877, -498, -4104, +128, +3146, +1756, -530, -2290, -350, +1519, -489, -802, -515, +198, -156, -758, -285, +204, -324, -438, -107, -349, +396, +198, +14, -193, -173, +148, +358, -427, -465, },
|
||||
{ +10582, +12562, -764, -4376, +91, +3406, +1737, -827, -2467, -415, +1769, -548, -939, -576, +195, -234, -826, -350, +190, -335, -585, -69, -435, +512, +288, -37, -200, -225, -129, +406, -34, -688, },
|
||||
{ +11306, +13237, -1039, -4536, -26, +3591, +1718, -1098, -2614, -469, +1956, -571, -1083, -632, +173, -288, -912, -416, +136, -318, -675, -9, -461, +518, +395, -47, -243, -278, -292, +231, +360, -602, },
|
||||
{ +12126, +13732, -1324, -4530, -169, +3644, +1688, -1289, -2721, -525, +2075, -627, -1199, -662, +140, -328, -973, -491, +59, -292, -671, -19, -361, +462, +431, -38, -247, -401, -359, +38, +478, -239, },
|
||||
{ +13174, +14048, -1834, -4351, -231, +3530, +1631, -1424, -2802, -592, +2109, -667, -1235, -700, +112, -376, -980, -597, +19, -296, -588, -73, -240, +447, +337, +12, -264, -517, -447, +2, +295, +152, },
|
||||
{ +13912, +14689, -2549, -4087, -276, +3213, +1690, -1511, -2845, -778, +2173, -760, -1162, -780, +32, -364, -1053, -628, -102, -130, -603, -57, -165, +414, +214, +87, -300, -593, -470, -60, +82, +348, },
|
||||
{ +15735, +14399, -3901, -3481, -192, +2964, +1469, -1601, -3017, -661, +2045, -910, -920, -980, +113, -561, -1039, -685, -48, -104, -573, +16, -218, +402, +105, +138, -407, -536, -465, -206, -44, +459, },
|
||||
{ +17507, +14116, -5426, -2751, -192, +2764, +1237, -1736, -3152, -642, +2056, -1032, -638, -1128, +153, -726, -1046, -569, -51, -121, -478, +1, -396, +547, -27, +111, -333, -501, -516, -340, +19, +317, },
|
||||
{ +19177, +13698, -6887, -1985, -243, +2671, +917, -1892, -3302, -557, +2196, -1213, -249, -1266, +144, -850, -933, -377, -271, +44, -521, -211, -328, +623, -195, +277, -239, -659, -459, -299, -118, +155, },
|
||||
{ +20876, +12896, -8162, -1221, -405, +2653, +504, -2083, -3425, -193, +2142, -1312, +212, -1534, +162, -820, -758, -426, -316, +25, -728, -166, -380, +694, -108, +401, -373, -670, -277, -461, -291, +192, },
|
||||
{ +22269, +11988, -9087, -514, -824, +2741, +141, -2324, -3367, +226, +1930, -1209, +579, -1900, +451, -737, -828, -320, -621, -37, -648, -370, -319, +1024, -166, +363, -328, -614, -348, -565, -333, +183, },
|
||||
{ +22436, +12036, -9756, +12, -1484, +2839, +192, -2651, -3134, +304, +2024, -1272, +960, -2016, +603, -606, -966, -471, -868, +179, -932, -258, -187, +1149, -274, +472, -264, -806, -269, -673, -333, +115, },
|
||||
{ +22976, +10737, -9315, +116, -2333, +3251, +276, -2916, -2945, +644, +1634, -1050, +1433, -2250, +799, -568, -1370, -360, -902, +24, -701, -266, -133, +1143, -162, +431, -320, -856, -257, -656, -421, +206, },
|
||||
{ +22156, +10670, -8825, +31, -3255, +3766, +558, -3195, -2534, +719, +1180, -629, +1637, -2399, +832, -768, -1413, -237, -975, +241, -538, -329, -188, +1315, -228, +362, -275, -900, -124, -757, -357, +287, },
|
||||
{ +21278, +9846, -7554, -398, -3803, +4159, +764, -3114, -2412, +808, +1003, -456, +1754, -2523, +558, -585, -1493, -124, -765, +323, -452, -399, -154, +1291, -276, +265, -134, -876, -130, -701, -238, +296, },
|
||||
{ +19461, +9711, -6136, -768, -4103, +4127, +1289, -3000, -2181, +873, +525, -145, +1756, -2618, +563, -484, -1372, +57, -547, +215, -362, -384, -264, +1161, -298, +275, -17, -893, -58, -554, -229, +265, },
|
||||
{ +17277, +9611, -4621, -998, -4208, +3875, +1658, -2589, -1841, +534, +392, -46, +1442, -2298, +316, -236, -1201, +192, -408, +85, -140, -396, -437, +993, -150, +202, +81, -782, +50, -474, -177, +231, },
|
||||
{ +14172, +9673, -3573, -888, -1759, +2079, +423, -1986, -1744, +555, +790, -69, +1145, -1353, +372, +59, -818, -47, -210, +85, -314, -228, -308, +651, -194, +55, +187, -514, -187, -359, -318, -25, },
|
||||
{ +12049, +9447, -2228, -1231, -1752, +1946, +840, -1465, -1503, +155, +682, -24, +949, -1008, +142, +192, -555, +85, -44, +97, -161, -260, -418, +506, -91, +11, +275, -348, -145, -288, -275, -84, },
|
||||
{ +10303, +9142, -1241, -1401, -1603, +1679, +1080, -926, -1326, -100, +605, -40, +855, -692, +34, +292, -275, +96, +72, +212, -127, -202, -437, +334, -5, -31, +233, -223, -120, -232, -200, -174, },
|
||||
{ +9017, +8838, -619, -1486, -1392, +1440, +1205, -522, -1133, -209, +558, -14, +754, -391, +50, +313, -71, +90, +164, +296, -77, -139, -435, +174, +4, -91, +133, -78, -72, -176, -176, -213, },
|
||||
{ +8142, +8597, -267, -1538, -1157, +1261, +1254, -258, -963, -244, +541, +75, +679, -187, +160, +270, +1, +140, +187, +302, -55, -83, -444, +4, -35, -132, +106, +52, -76, -189, -49, -190, },
|
||||
{ +7622, +8383, -48, -1578, -899, +1181, +1234, -71, -944, -214, +551, +137, +707, -144, +260, +320, -94, +109, +245, +239, -123, -2, -426, -196, -30, -52, +38, -6, +17, -57, -187, -8, },
|
||||
{ +7332, +8319, +57, -1601, -631, +1238, +1135, +21, -967, -327, +659, +76, +672, +38, +199, +349, -41, -97, +208, +331, -233, -127, -295, -155, -164, -26, -20, -11, +90, -178, +192, +245, },
|
||||
{ +7317, +8418, -7, -1642, -367, +1419, +1147, -111, -974, -384, +550, +84, +565, +94, +364, +313, -68, -10, +86, +197, -143, -149, -385, -44, -17, -229, -53, +121, -114, +140, +435, -168, },
|
||||
{ +7367, +8733, -36, -1826, -124, +1616, +1200, -184, -1158, -452, +570, -113, +408, +131, +319, +450, -22, -49, +185, +112, -380, -100, -266, -117, -72, -71, +10, -213, +51, +444, +58, -226, },
|
||||
{ +7780, +9068, -243, -2044, +77, +1858, +1206, -274, -1289, -551, +596, -178, +200, -29, +344, +432, -90, +103, +283, +41, -414, -210, -414, +8, +0, -140, +15, -152, +111, +292, -78, -206, },
|
||||
{ +8345, +9580, -574, -2318, +346, +2040, +1168, -408, -1458, -527, +676, -258, +1, -124, +320, +310, -183, +87, +370, +84, -415, -236, -480, -21, -74, -20, +45, -230, +106, +351, -132, -434, },
|
||||
{ +9094, +10154, -1025, -2554, +632, +2229, +994, -595, -1621, -437, +874, -416, -132, -257, +352, +267, -420, +47, +366, +3, -398, -143, -510, +98, -129, -195, +92, -138, -60, +248, +111, -492, },
|
||||
{ +10041, +10768, -1614, -2719, +916, +2381, +756, -898, -1763, -304, +1151, -599, -325, -310, +358, +208, -536, -91, +282, -46, -529, -37, -456, +197, -31, -321, -13, -176, -156, +116, +291, -363, },
|
||||
{ +11107, +11413, -2234, -2770, +1108, +2450, +521, -1208, -1885, -154, +1431, -759, -498, -367, +419, +97, -627, -128, +121, -90, -589, -28, -356, +273, +33, -279, -81, -291, -313, -16, +331, +7, },
|
||||
{ +12280, +12021, -2906, -2717, +1134, +2377, +314, -1466, -2001, -37, +1641, -947, -595, -443, +445, +1, -745, -215, +22, -112, -652, -10, -293, +280, +22, -180, -159, -383, -467, -188, +208, +307, },
|
||||
{ +13475, +12606, -3625, -2517, +1014, +2161, +183, -1625, -2084, +8, +1791, -1103, -612, -507, +435, -57, -847, -324, -41, -71, -678, +57, -282, +263, -37, -62, -209, -455, -522, -398, +113, +417, },
|
||||
{ +14812, +13011, -4511, -2154, +806, +1834, +70, -1725, -2175, +16, +1867, -1208, -532, -590, +417, -130, -929, -396, -58, -66, -662, +121, -368, +291, -144, -1, -180, -513, -541, -616, +106, +375, },
|
||||
{ +16380, +13067, -5650, -1592, +605, +1495, -131, -1780, -2290, +40, +2010, -1367, -305, -662, +396, -248, -905, -354, -222, +46, -645, +6, -354, +338, -292, +124, -80, -658, -520, -638, -12, +271, },
|
||||
{ +17982, +12702, -6846, -878, +421, +1177, -463, -1808, -2473, +274, +2108, -1572, +121, -826, +355, -267, -731, -513, -308, +176, -908, +60, -376, +313, -223, +268, -136, -780, -340, -699, -278, +268, },
|
||||
{ +19157, +12247, -7860, -132, +149, +912, -724, -1957, -2452, +536, +2076, -1557, +521, -1124, +525, -122, -855, -442, -535, +76, -863, -69, -379, +601, -217, +257, -79, -739, -389, -790, -342, +200, },
|
||||
{ +19711, +11522, -8169, +401, -329, +905, -928, -2048, -2370, +752, +2017, -1467, +767, -1174, +691, -185, -760, -679, -713, +177, -1039, -25, -158, +692, -291, +423, -59, -878, -408, -762, -397, +110, },
|
||||
{ +19465, +10779, -7703, +460, -893, +1187, -817, -2234, -2246, +1019, +1640, -1190, +1218, -1460, +857, -119, -1111, -511, -817, +81, -770, -57, -137, +805, -201, +370, -136, -863, -389, -713, -437, +67, },
|
||||
{ +18333, +10203, -6584, +85, -1382, +1684, -563, -2346, -2049, +928, +1357, -671, +1226, -1413, +733, -229, -1009, -521, -666, +153, -618, -188, -102, +925, -315, +267, -79, -795, -334, -687, -385, +130, },
|
||||
{ +16441, +9946, -5138, -473, -1669, +2005, -91, -2252, -1996, +837, +1093, -412, +1392, -1542, +535, -11, -1080, -203, -475, +134, -478, -260, -105, +753, -285, +165, +32, -670, -301, -515, -295, +22, },
|
||||
{ +13089, +9529, -4017, -237, +483, +1183, -334, -1490, -1552, +647, +1264, -471, +629, -605, +562, -36, -643, -387, -501, +84, -371, -60, -86, +640, -100, +137, -1, -574, -350, -440, -324, -139, },
|
||||
{ +11898, +9621, -3405, -1165, +243, +1679, +179, -1312, -1709, +64, +1140, -239, +692, -458, +479, +250, -561, -188, -174, +41, -320, -165, -304, +589, -101, -43, +124, -435, -309, -354, -282, -103, },
|
||||
{ +10997, +9658, -2780, -1802, -26, +1783, +569, -947, -1662, -248, +967, -127, +729, -294, +466, +390, -354, -145, +46, +95, -329, -112, -479, +387, +17, -149, +90, -226, -277, -280, -149, -147, },
|
||||
{ +10403, +9825, -2433, -2099, -53, +1814, +738, -808, -1574, -372, +873, -167, +701, -213, +490, +502, -294, -53, +103, +45, -399, -57, -537, +124, +113, -120, +21, -185, -188, -248, -114, -74, },
|
||||
{ +10392, +9882, -2497, -2112, +324, +1944, +702, -937, -1618, -322, +898, -262, +449, -113, +460, +482, -188, -10, +186, -20, -556, -166, -451, +104, +9, -20, +13, -185, -148, -338, -76, +107, },
|
||||
{ +10862, +9834, -2867, -1780, +884, +2063, +422, -1170, -1644, -165, +979, -389, +198, -268, +566, +314, -335, +218, +172, -127, -560, -306, -505, +230, +61, -85, -17, -114, -234, -393, -69, +141, },
|
||||
{ +11578, +9958, -3241, -1267, +1305, +1835, +61, -1353, -1523, +172, +1158, -610, -59, -256, +501, +100, -434, +87, +156, -173, -554, -216, -452, +238, -8, +72, -57, -233, -279, -439, -54, +128, },
|
||||
{ +12590, +10282, -3669, -831, +1213, +1299, -204, -1324, -1305, +642, +1310, -929, -150, -265, +378, -166, -511, -226, -25, -77, -591, -30, -316, +195, -77, +192, -79, -444, -281, -501, -173, +158, },
|
||||
{ +13820, +10820, -4370, -641, +688, +719, -208, -1170, -1120, +1054, +1422, -1268, +60, -395, +126, -158, -785, -547, -55, -15, -622, +139, -270, +187, +11, +211, -203, -518, -250, -711, -272, +251, },
|
||||
{ +14973, +10840, -5035, -319, +215, +327, -180, -1056, -1001, +1357, +1436, -1376, +373, -623, +75, -133, -1012, -696, -148, +89, -603, +166, -149, +257, +111, +201, -249, -478, -375, -806, -271, +241, },
|
||||
{ +15155, +10582, -5238, +16, +115, +211, -361, -1141, -1066, +1374, +1522, -1325, +512, -561, +33, -137, -860, -968, -261, +174, -757, +281, -16, +297, +93, +272, -236, -631, -384, -770, -341, +145, },
|
||||
{ +14475, +9926, -4814, +144, +323, +523, -528, -1336, -1319, +1198, +1433, -1025, +675, -646, +308, -70, -834, -739, -508, +86, -551, +161, +29, +400, +61, +248, -225, -631, -389, -587, -390, -22, },
|
||||
{ +11096, +9607, -2841, -481, +1236, +612, -162, -1033, -885, +1058, +1197, -645, +47, -150, +202, +5, -433, -652, -254, -16, -605, +40, +33, +357, +315, +288, -273, -769, -633, -453, +2, +259, },
|
||||
},
|
||||
|
||||
/* HRIR Delays */
|
||||
{ 12, 12, 13, 14, 14, 14, 13, 12, 11, 10, 10, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 18, 18, 18, 18, 18, 17, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 20, 20, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 21, 22, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 24, 24, 24, 24, 23, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 24, 25, 26, 26, 26, 26, 26, 25, 24, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 27, 26, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 24, 25, 26, 26, 26, 26, 26, 25, 24, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23, 24, 24, 24, 24, 24, 23, 22, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 21, 22, 22, 22, 22, 22, 21, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 6, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18, 19, 20, 20, 20, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 5, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 18, 18, 18, 18, 18, 17, 16, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 7, 6, 6, 6, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 8, 8, 8, 8, 8, 9, 10, 11, 12, 13, 14, 14, 14, 13, 12, 11, 10, 10, 10, 11, 12, }
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef INPROGEXT_H
|
||||
#define INPROGEXT_H
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_loopback2
|
||||
#define ALC_SOFT_loopback2 1
|
||||
#define ALC_AMBISONIC_LAYOUT_SOFT 0xfff0
|
||||
#define ALC_AMBISONIC_SCALING_SOFT 0xfff1
|
||||
#define ALC_AMBISONIC_ORDER_SOFT 0xfff2
|
||||
#define ALC_MAX_AMBISONIC_ORDER_SOFT 0xfff3
|
||||
|
||||
#define ALC_BFORMAT3D_SOFT 0x1508
|
||||
|
||||
/* Ambisonic layouts */
|
||||
#define ALC_ACN_SOFT 0xfff4
|
||||
#define ALC_FUMA_SOFT 0xfff5
|
||||
|
||||
/* Ambisonic scalings (normalization) */
|
||||
/*#define ALC_FUMA_SOFT*/
|
||||
#define ALC_SN3D_SOFT 0xfff6
|
||||
#define ALC_N3D_SOFT 0xfff7
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_map_buffer
|
||||
#define AL_SOFT_map_buffer 1
|
||||
typedef unsigned int ALbitfieldSOFT;
|
||||
#define AL_MAP_READ_BIT_SOFT 0x00000001
|
||||
#define AL_MAP_WRITE_BIT_SOFT 0x00000002
|
||||
#define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004
|
||||
#define AL_PRESERVE_DATA_BIT_SOFT 0x00000008
|
||||
typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
|
||||
typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
|
||||
typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer);
|
||||
typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
|
||||
AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
|
||||
AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer);
|
||||
AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_events
|
||||
#define AL_SOFT_events 1
|
||||
#define AL_EVENT_CALLBACK_FUNCTION_SOFT 0x1220
|
||||
#define AL_EVENT_CALLBACK_USER_PARAM_SOFT 0x1221
|
||||
#define AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT 0x1222
|
||||
#define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x1223
|
||||
#define AL_EVENT_TYPE_ERROR_SOFT 0x1224
|
||||
#define AL_EVENT_TYPE_PERFORMANCE_SOFT 0x1225
|
||||
#define AL_EVENT_TYPE_DEPRECATED_SOFT 0x1226
|
||||
#define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x1227
|
||||
typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param,
|
||||
ALsizei length, const ALchar *message,
|
||||
void *userParam);
|
||||
typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable);
|
||||
typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam);
|
||||
typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
|
||||
typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable);
|
||||
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam);
|
||||
AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname);
|
||||
AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_buffer_layers
|
||||
#define AL_SOFT_buffer_layers
|
||||
typedef void (AL_APIENTRY*LPALSOURCEQUEUEBUFFERLAYERSSOFT)(ALuint src, ALsizei nb, const ALuint *buffers);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* INPROGEXT_H */
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define DECL_FORMAT(x, y, z) __attribute__((format(x, (y), (z))))
|
||||
#else
|
||||
#define DECL_FORMAT(x, y, z)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern FILE *LogFile;
|
||||
|
||||
#if defined(__GNUC__) && !defined(_WIN32)
|
||||
#define AL_PRINT(T, MSG, ...) fprintf(LogFile, "AL lib: %s %s: "MSG, T, __FUNCTION__ , ## __VA_ARGS__)
|
||||
#else
|
||||
void al_print(const char *type, const char *func, const char *fmt, ...) DECL_FORMAT(printf, 3,4);
|
||||
#define AL_PRINT(T, ...) al_print((T), __FUNCTION__, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#define LOG_ANDROID(T, MSG, ...) __android_log_print(T, "openal", "AL lib: %s: "MSG, __FUNCTION__ , ## __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_ANDROID(T, MSG, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
enum LogLevel {
|
||||
NoLog,
|
||||
LogError,
|
||||
LogWarning,
|
||||
LogTrace,
|
||||
LogRef
|
||||
};
|
||||
extern enum LogLevel LogLevel;
|
||||
|
||||
#define TRACEREF(...) do { \
|
||||
if(LogLevel >= LogRef) \
|
||||
AL_PRINT("(--)", __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define TRACE(...) do { \
|
||||
if(LogLevel >= LogTrace) \
|
||||
AL_PRINT("(II)", __VA_ARGS__); \
|
||||
LOG_ANDROID(ANDROID_LOG_DEBUG, __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define WARN(...) do { \
|
||||
if(LogLevel >= LogWarning) \
|
||||
AL_PRINT("(WW)", __VA_ARGS__); \
|
||||
LOG_ANDROID(ANDROID_LOG_WARN, __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#define ERR(...) do { \
|
||||
if(LogLevel >= LogError) \
|
||||
AL_PRINT("(EE)", __VA_ARGS__); \
|
||||
LOG_ANDROID(ANDROID_LOG_ERROR, __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LOGGING_H */
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "mastering.h"
|
||||
#include "alu.h"
|
||||
#include "almalloc.h"
|
||||
#include "static_assert.h"
|
||||
#include "math_defs.h"
|
||||
|
||||
|
||||
/* Early MSVC lacks round/roundf */
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1800
|
||||
static double round(double val)
|
||||
{
|
||||
if(val < 0.0)
|
||||
return ceil(val-0.5);
|
||||
return floor(val+0.5);
|
||||
}
|
||||
#define roundf(f) ((float)round((float)(f)))
|
||||
#endif
|
||||
|
||||
|
||||
/* These structures assume BUFFERSIZE is a power of 2. */
|
||||
static_assert((BUFFERSIZE & (BUFFERSIZE-1)) == 0, "BUFFERSIZE is not a power of 2");
|
||||
|
||||
typedef struct SlidingHold {
|
||||
ALfloat Values[BUFFERSIZE];
|
||||
ALsizei Expiries[BUFFERSIZE];
|
||||
ALsizei LowerIndex;
|
||||
ALsizei UpperIndex;
|
||||
ALsizei Length;
|
||||
} SlidingHold;
|
||||
|
||||
/* General topology and basic automation was based on the following paper:
|
||||
*
|
||||
* D. Giannoulis, M. Massberg and J. D. Reiss,
|
||||
* "Parameter Automation in a Dynamic Range Compressor,"
|
||||
* Journal of the Audio Engineering Society, v61 (10), Oct. 2013
|
||||
*
|
||||
* Available (along with supplemental reading) at:
|
||||
*
|
||||
* http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/
|
||||
*/
|
||||
typedef struct Compressor {
|
||||
ALsizei NumChans;
|
||||
ALuint SampleRate;
|
||||
|
||||
struct {
|
||||
ALuint Knee : 1;
|
||||
ALuint Attack : 1;
|
||||
ALuint Release : 1;
|
||||
ALuint PostGain : 1;
|
||||
ALuint Declip : 1;
|
||||
} Auto;
|
||||
|
||||
ALsizei LookAhead;
|
||||
|
||||
ALfloat PreGain;
|
||||
ALfloat PostGain;
|
||||
|
||||
ALfloat Threshold;
|
||||
ALfloat Slope;
|
||||
ALfloat Knee;
|
||||
|
||||
ALfloat Attack;
|
||||
ALfloat Release;
|
||||
|
||||
alignas(16) ALfloat SideChain[2*BUFFERSIZE];
|
||||
alignas(16) ALfloat CrestFactor[BUFFERSIZE];
|
||||
|
||||
SlidingHold *Hold;
|
||||
ALfloat (*Delay)[BUFFERSIZE];
|
||||
ALsizei DelayIndex;
|
||||
|
||||
ALfloat CrestCoeff;
|
||||
ALfloat GainEstimate;
|
||||
ALfloat AdaptCoeff;
|
||||
|
||||
ALfloat LastPeakSq;
|
||||
ALfloat LastRmsSq;
|
||||
ALfloat LastRelease;
|
||||
ALfloat LastAttack;
|
||||
ALfloat LastGainDev;
|
||||
} Compressor;
|
||||
|
||||
|
||||
/* This sliding hold follows the input level with an instant attack and a
|
||||
* fixed duration hold before an instant release to the next highest level.
|
||||
* It is a sliding window maximum (descending maxima) implementation based on
|
||||
* Richard Harter's ascending minima algorithm available at:
|
||||
*
|
||||
* http://www.richardhartersworld.com/cri/2001/slidingmin.html
|
||||
*/
|
||||
static ALfloat UpdateSlidingHold(SlidingHold *Hold, const ALsizei i, const ALfloat in)
|
||||
{
|
||||
const ALsizei mask = BUFFERSIZE - 1;
|
||||
const ALsizei length = Hold->Length;
|
||||
ALfloat *restrict values = Hold->Values;
|
||||
ALsizei *restrict expiries = Hold->Expiries;
|
||||
ALsizei lowerIndex = Hold->LowerIndex;
|
||||
ALsizei upperIndex = Hold->UpperIndex;
|
||||
|
||||
if(i >= expiries[upperIndex])
|
||||
upperIndex = (upperIndex + 1) & mask;
|
||||
|
||||
if(in >= values[upperIndex])
|
||||
{
|
||||
values[upperIndex] = in;
|
||||
expiries[upperIndex] = i + length;
|
||||
lowerIndex = upperIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
do {
|
||||
do {
|
||||
if(!(in >= values[lowerIndex]))
|
||||
goto found_place;
|
||||
} while(lowerIndex--);
|
||||
lowerIndex = mask;
|
||||
} while(1);
|
||||
found_place:
|
||||
|
||||
lowerIndex = (lowerIndex + 1) & mask;
|
||||
values[lowerIndex] = in;
|
||||
expiries[lowerIndex] = i + length;
|
||||
}
|
||||
|
||||
Hold->LowerIndex = lowerIndex;
|
||||
Hold->UpperIndex = upperIndex;
|
||||
|
||||
return values[upperIndex];
|
||||
}
|
||||
|
||||
static void ShiftSlidingHold(SlidingHold *Hold, const ALsizei n)
|
||||
{
|
||||
const ALsizei lowerIndex = Hold->LowerIndex;
|
||||
ALsizei *restrict expiries = Hold->Expiries;
|
||||
ALsizei i = Hold->UpperIndex;
|
||||
|
||||
if(lowerIndex < i)
|
||||
{
|
||||
for(;i < BUFFERSIZE;i++)
|
||||
expiries[i] -= n;
|
||||
i = 0;
|
||||
}
|
||||
for(;i < lowerIndex;i++)
|
||||
expiries[i] -= n;
|
||||
|
||||
expiries[i] -= n;
|
||||
}
|
||||
|
||||
/* Multichannel compression is linked via the absolute maximum of all
|
||||
* channels.
|
||||
*/
|
||||
static void LinkChannels(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
sideChain[index + i] = 0.0f;
|
||||
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
ALsizei offset = index;
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
sideChain[offset] = maxf(sideChain[offset], fabsf(OutBuffer[c][i]));
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This calculates the squared crest factor of the control signal for the
|
||||
* basic automation of the attack/release times. As suggested by the paper,
|
||||
* it uses an instantaneous squared peak detector and a squared RMS detector
|
||||
* both with 200ms release times.
|
||||
*/
|
||||
static void CrestDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALfloat a_crest = Comp->CrestCoeff;
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
const ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALfloat *restrict crestFactor = Comp->CrestFactor;
|
||||
ALfloat y2_peak = Comp->LastPeakSq;
|
||||
ALfloat y2_rms = Comp->LastRmsSq;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat x_abs = sideChain[index + i];
|
||||
ALfloat x2 = maxf(0.000001f, x_abs * x_abs);
|
||||
|
||||
y2_peak = maxf(x2, lerp(x2, y2_peak, a_crest));
|
||||
y2_rms = lerp(x2, y2_rms, a_crest);
|
||||
crestFactor[i] = y2_peak / y2_rms;
|
||||
}
|
||||
|
||||
Comp->LastPeakSq = y2_peak;
|
||||
Comp->LastRmsSq = y2_rms;
|
||||
}
|
||||
|
||||
/* The side-chain starts with a simple peak detector (based on the absolute
|
||||
* value of the incoming signal) and performs most of its operations in the
|
||||
* log domain.
|
||||
*/
|
||||
static void PeakDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALuint offset = index + i;
|
||||
const ALfloat x_abs = sideChain[offset];
|
||||
|
||||
sideChain[offset] = logf(maxf(0.000001f, x_abs));
|
||||
}
|
||||
}
|
||||
|
||||
/* An optional hold can be used to extend the peak detector so it can more
|
||||
* solidly detect fast transients. This is best used when operating as a
|
||||
* limiter.
|
||||
*/
|
||||
static void PeakHoldDetector(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const ALsizei index = Comp->LookAhead;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
SlidingHold *hold = Comp->Hold;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALsizei offset = index + i;
|
||||
const ALfloat x_abs = sideChain[offset];
|
||||
const ALfloat x_G = logf(maxf(0.000001f, x_abs));
|
||||
|
||||
sideChain[offset] = UpdateSlidingHold(hold, i, x_G);
|
||||
}
|
||||
|
||||
ShiftSlidingHold(hold, SamplesToDo);
|
||||
}
|
||||
|
||||
/* This is the heart of the feed-forward compressor. It operates in the log
|
||||
* domain (to better match human hearing) and can apply some basic automation
|
||||
* to knee width, attack/release times, make-up/post gain, and clipping
|
||||
* reduction.
|
||||
*/
|
||||
static void GainCompressor(Compressor *Comp, const ALsizei SamplesToDo)
|
||||
{
|
||||
const bool autoKnee = Comp->Auto.Knee;
|
||||
const bool autoAttack = Comp->Auto.Attack;
|
||||
const bool autoRelease = Comp->Auto.Release;
|
||||
const bool autoPostGain = Comp->Auto.PostGain;
|
||||
const bool autoDeclip = Comp->Auto.Declip;
|
||||
const ALsizei lookAhead = Comp->LookAhead;
|
||||
const ALfloat threshold = Comp->Threshold;
|
||||
const ALfloat slope = Comp->Slope;
|
||||
const ALfloat attack = Comp->Attack;
|
||||
const ALfloat release = Comp->Release;
|
||||
const ALfloat c_est = Comp->GainEstimate;
|
||||
const ALfloat a_adp = Comp->AdaptCoeff;
|
||||
const ALfloat *restrict crestFactor = Comp->CrestFactor;
|
||||
ALfloat *restrict sideChain = Comp->SideChain;
|
||||
ALfloat postGain = Comp->PostGain;
|
||||
ALfloat knee = Comp->Knee;
|
||||
ALfloat t_att = attack;
|
||||
ALfloat t_rel = release - attack;
|
||||
ALfloat a_att = expf(-1.0f / t_att);
|
||||
ALfloat a_rel = expf(-1.0f / t_rel);
|
||||
ALfloat y_1 = Comp->LastRelease;
|
||||
ALfloat y_L = Comp->LastAttack;
|
||||
ALfloat c_dev = Comp->LastGainDev;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
const ALfloat y2_crest = crestFactor[i];
|
||||
const ALfloat x_G = sideChain[lookAhead + i];
|
||||
const ALfloat x_over = x_G - threshold;
|
||||
ALfloat knee_h;
|
||||
ALfloat y_G;
|
||||
ALfloat x_L;
|
||||
|
||||
if(autoKnee)
|
||||
knee = maxf(0.0f, 2.5f * (c_dev + c_est));
|
||||
knee_h = 0.5f * knee;
|
||||
|
||||
/* This is the gain computer. It applies a static compression curve
|
||||
* to the control signal.
|
||||
*/
|
||||
if(x_over <= -knee_h)
|
||||
y_G = 0.0f;
|
||||
else if(fabsf(x_over) < knee_h)
|
||||
y_G = (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee);
|
||||
else
|
||||
y_G = x_over;
|
||||
|
||||
x_L = -slope * y_G;
|
||||
|
||||
if(autoAttack)
|
||||
{
|
||||
t_att = 2.0f * attack / y2_crest;
|
||||
a_att = expf(-1.0f / t_att);
|
||||
}
|
||||
|
||||
if(autoRelease)
|
||||
{
|
||||
t_rel = 2.0f * release / y2_crest - t_att;
|
||||
a_rel = expf(-1.0f / t_rel);
|
||||
}
|
||||
|
||||
/* Gain smoothing (ballistics) is done via a smooth decoupled peak
|
||||
* detector. The attack time is subtracted from the release time
|
||||
* above to compensate for the chained operating mode.
|
||||
*/
|
||||
y_1 = maxf(x_L, lerp(x_L, y_1, a_rel));
|
||||
y_L = lerp(y_1, y_L, a_att);
|
||||
|
||||
/* Knee width and make-up gain automation make use of a smoothed
|
||||
* measurement of deviation between the control signal and estimate.
|
||||
* The estimate is also used to bias the measurement to hot-start its
|
||||
* average.
|
||||
*/
|
||||
c_dev = lerp(-y_L - c_est, c_dev, a_adp);
|
||||
|
||||
if(autoPostGain)
|
||||
{
|
||||
/* Clipping reduction is only viable when make-up gain is being
|
||||
* automated. It modifies the deviation to further attenuate the
|
||||
* control signal when clipping is detected. The adaptation
|
||||
* time is sufficiently long enough to suppress further clipping
|
||||
* at the same output level.
|
||||
*/
|
||||
if(autoDeclip)
|
||||
c_dev = maxf(c_dev, sideChain[i] - y_L - threshold - c_est);
|
||||
|
||||
postGain = -(c_dev + c_est);
|
||||
}
|
||||
|
||||
sideChain[i] = expf(postGain - y_L);
|
||||
}
|
||||
|
||||
Comp->LastRelease = y_1;
|
||||
Comp->LastAttack = y_L;
|
||||
Comp->LastGainDev = c_dev;
|
||||
}
|
||||
|
||||
/* Combined with the hold time, a look-ahead delay can improve handling of
|
||||
* fast transients by allowing the envelope time to converge prior to
|
||||
* reaching the offending impulse. This is best used when operating as a
|
||||
* limiter.
|
||||
*/
|
||||
static void SignalDelay(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei mask = BUFFERSIZE - 1;
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
const ALsizei indexIn = Comp->DelayIndex;
|
||||
const ALsizei indexOut = Comp->DelayIndex - Comp->LookAhead;
|
||||
ALfloat (*restrict delay)[BUFFERSIZE] = Comp->Delay;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
{
|
||||
ALfloat sig = OutBuffer[c][i];
|
||||
|
||||
OutBuffer[c][i] = delay[c][(indexOut + i) & mask];
|
||||
delay[c][(indexIn + i) & mask] = sig;
|
||||
}
|
||||
}
|
||||
|
||||
Comp->DelayIndex = (indexIn + SamplesToDo) & mask;
|
||||
}
|
||||
|
||||
/* The compressor is initialized with the following settings:
|
||||
*
|
||||
* NumChans - Number of channels to process.
|
||||
* SampleRate - Sample rate to process.
|
||||
* AutoKnee - Whether to automate the knee width parameter.
|
||||
* AutoAttack - Whether to automate the attack time parameter.
|
||||
* AutoRelease - Whether to automate the release time parameter.
|
||||
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
|
||||
* AutoDeclip - Whether to automate clipping reduction. Ignored when
|
||||
* not automating make-up gain.
|
||||
* LookAheadTime - Look-ahead time (in seconds).
|
||||
* HoldTime - Peak hold-time (in seconds).
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Make-up gain applied after compression (in dB).
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* Ratio - Compression ratio (x:1). Set to INFINITY for true
|
||||
* limiting. Ignored when automating knee width.
|
||||
* KneeDb - Knee width (in dB). Ignored when automating knee
|
||||
* width.
|
||||
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
|
||||
* automating attack time.
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
const ALfloat HoldTime, const ALfloat PreGainDb,
|
||||
const ALfloat PostGainDb, const ALfloat ThresholdDb,
|
||||
const ALfloat Ratio, const ALfloat KneeDb,
|
||||
const ALfloat AttackTime, const ALfloat ReleaseTime)
|
||||
{
|
||||
Compressor *Comp;
|
||||
ALsizei lookAhead;
|
||||
ALsizei hold;
|
||||
size_t size;
|
||||
|
||||
lookAhead = (ALsizei)clampf(roundf(LookAheadTime*SampleRate), 0.0f, BUFFERSIZE-1);
|
||||
hold = (ALsizei)clampf(roundf(HoldTime*SampleRate), 0.0f, BUFFERSIZE-1);
|
||||
/* The sliding hold implementation doesn't handle a length of 1. A 1-sample
|
||||
* hold is useless anyway, it would only ever give back what was just given
|
||||
* to it.
|
||||
*/
|
||||
if(hold == 1)
|
||||
hold = 0;
|
||||
|
||||
size = sizeof(*Comp);
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
size += sizeof(*Comp->Delay) * NumChans;
|
||||
if(hold > 0)
|
||||
size += sizeof(*Comp->Hold);
|
||||
}
|
||||
|
||||
Comp = al_calloc(16, size);
|
||||
Comp->NumChans = NumChans;
|
||||
Comp->SampleRate = SampleRate;
|
||||
Comp->Auto.Knee = AutoKnee;
|
||||
Comp->Auto.Attack = AutoAttack;
|
||||
Comp->Auto.Release = AutoRelease;
|
||||
Comp->Auto.PostGain = AutoPostGain;
|
||||
Comp->Auto.Declip = AutoPostGain && AutoDeclip;
|
||||
Comp->LookAhead = lookAhead;
|
||||
Comp->PreGain = powf(10.0f, PreGainDb / 20.0f);
|
||||
Comp->PostGain = PostGainDb * logf(10.0f) / 20.0f;
|
||||
Comp->Threshold = ThresholdDb * logf(10.0f) / 20.0f;
|
||||
Comp->Slope = 1.0f / maxf(1.0f, Ratio) - 1.0f;
|
||||
Comp->Knee = maxf(0.0f, KneeDb * logf(10.0f) / 20.0f);
|
||||
Comp->Attack = maxf(1.0f, AttackTime * SampleRate);
|
||||
Comp->Release = maxf(1.0f, ReleaseTime * SampleRate);
|
||||
|
||||
/* Knee width automation actually treats the compressor as a limiter. By
|
||||
* varying the knee width, it can effectively be seen as applying
|
||||
* compression over a wide range of ratios.
|
||||
*/
|
||||
if(AutoKnee)
|
||||
Comp->Slope = -1.0f;
|
||||
|
||||
if(lookAhead > 0)
|
||||
{
|
||||
if(hold > 0)
|
||||
{
|
||||
Comp->Hold = (SlidingHold*)(Comp + 1);
|
||||
Comp->Hold->Values[0] = -HUGE_VALF;
|
||||
Comp->Hold->Expiries[0] = hold;
|
||||
Comp->Hold->Length = hold;
|
||||
Comp->Delay = (ALfloat(*)[BUFFERSIZE])(Comp->Hold + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Comp->Delay = (ALfloat(*)[BUFFERSIZE])(Comp + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Comp->CrestCoeff = expf(-1.0f / (0.200f * SampleRate)); // 200ms
|
||||
Comp->GainEstimate = Comp->Threshold * -0.5f * Comp->Slope;
|
||||
Comp->AdaptCoeff = expf(-1.0f / (2.0f * SampleRate)); // 2s
|
||||
|
||||
return Comp;
|
||||
}
|
||||
|
||||
void ApplyCompression(Compressor *Comp, const ALsizei SamplesToDo, ALfloat (*restrict OutBuffer)[BUFFERSIZE])
|
||||
{
|
||||
const ALsizei numChans = Comp->NumChans;
|
||||
const ALfloat preGain = Comp->PreGain;
|
||||
ALfloat *restrict sideChain;
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
ASSUME(numChans > 0);
|
||||
|
||||
if(preGain != 1.0f)
|
||||
{
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= preGain;
|
||||
}
|
||||
}
|
||||
|
||||
LinkChannels(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
if(Comp->Auto.Attack || Comp->Auto.Release)
|
||||
CrestDetector(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Hold)
|
||||
PeakHoldDetector(Comp, SamplesToDo);
|
||||
else
|
||||
PeakDetector(Comp, SamplesToDo);
|
||||
|
||||
GainCompressor(Comp, SamplesToDo);
|
||||
|
||||
if(Comp->Delay)
|
||||
SignalDelay(Comp, SamplesToDo, OutBuffer);
|
||||
|
||||
sideChain = Comp->SideChain;
|
||||
for(c = 0;c < numChans;c++)
|
||||
{
|
||||
for(i = 0;i < SamplesToDo;i++)
|
||||
OutBuffer[c][i] *= sideChain[i];
|
||||
}
|
||||
|
||||
memmove(sideChain, sideChain+SamplesToDo, Comp->LookAhead*sizeof(ALfloat));
|
||||
}
|
||||
|
||||
|
||||
ALsizei GetCompressorLookAhead(const Compressor *Comp)
|
||||
{ return Comp->LookAhead; }
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef MASTERING_H
|
||||
#define MASTERING_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
/* For BUFFERSIZE. */
|
||||
#include "alMain.h"
|
||||
|
||||
struct Compressor;
|
||||
|
||||
/* The compressor is initialized with the following settings:
|
||||
*
|
||||
* NumChans - Number of channels to process.
|
||||
* SampleRate - Sample rate to process.
|
||||
* AutoKnee - Whether to automate the knee width parameter.
|
||||
* AutoAttack - Whether to automate the attack time parameter.
|
||||
* AutoRelease - Whether to automate the release time parameter.
|
||||
* AutoPostGain - Whether to automate the make-up (post) gain parameter.
|
||||
* AutoDeclip - Whether to automate clipping reduction. Ignored when
|
||||
* not automating make-up gain.
|
||||
* LookAheadTime - Look-ahead time (in seconds).
|
||||
* HoldTime - Peak hold-time (in seconds).
|
||||
* PreGainDb - Gain applied before detection (in dB).
|
||||
* PostGainDb - Make-up gain applied after compression (in dB).
|
||||
* ThresholdDb - Triggering threshold (in dB).
|
||||
* Ratio - Compression ratio (x:1). Set to INFINIFTY for true
|
||||
* limiting. Ignored when automating knee width.
|
||||
* KneeDb - Knee width (in dB). Ignored when automating knee
|
||||
* width.
|
||||
* AttackTimeMin - Attack time (in seconds). Acts as a maximum when
|
||||
* automating attack time.
|
||||
* ReleaseTimeMin - Release time (in seconds). Acts as a maximum when
|
||||
* automating release time.
|
||||
*/
|
||||
struct Compressor* CompressorInit(const ALsizei NumChans, const ALuint SampleRate,
|
||||
const ALboolean AutoKnee, const ALboolean AutoAttack,
|
||||
const ALboolean AutoRelease, const ALboolean AutoPostGain,
|
||||
const ALboolean AutoDeclip, const ALfloat LookAheadTime,
|
||||
const ALfloat HoldTime, const ALfloat PreGainDb,
|
||||
const ALfloat PostGainDb, const ALfloat ThresholdDb,
|
||||
const ALfloat Ratio, const ALfloat KneeDb,
|
||||
const ALfloat AttackTime, const ALfloat ReleaseTime);
|
||||
|
||||
void ApplyCompression(struct Compressor *Comp, const ALsizei SamplesToDo,
|
||||
ALfloat (*restrict OutBuffer)[BUFFERSIZE]);
|
||||
|
||||
ALsizei GetCompressorLookAhead(const struct Compressor *Comp);
|
||||
|
||||
#endif /* MASTERING_H */
|
||||
-822
@@ -1,822 +0,0 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alSource.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alListener.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "alu.h"
|
||||
#include "bs2b.h"
|
||||
|
||||
|
||||
static __inline ALfloat point32(const ALfloat *vals, ALint step, ALint frac)
|
||||
{ return vals[0]; (void)step; (void)frac; }
|
||||
static __inline ALfloat lerp32(const ALfloat *vals, ALint step, ALint frac)
|
||||
{ return lerp(vals[0], vals[step], frac * (1.0f/FRACTIONONE)); }
|
||||
static __inline ALfloat cubic32(const ALfloat *vals, ALint step, ALint frac)
|
||||
{ return cubic(vals[-step], vals[0], vals[step], vals[step+step],
|
||||
frac * (1.0f/FRACTIONONE)); }
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define LIKELY(x) (x)
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_H)
|
||||
#include <arm_neon.h>
|
||||
|
||||
static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
|
||||
ALfloat (*RESTRICT Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
for(c = 0;c < HRIR_LENGTH;c += 2)
|
||||
{
|
||||
const ALuint o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALuint o1 = (o0+1)&HRIR_MASK;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2],
|
||||
ALfloat (*RESTRICT Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALuint c;
|
||||
for(c = 0;c < HRIR_LENGTH;c++)
|
||||
{
|
||||
const ALuint off = (Offset+c)&HRIR_MASK;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define DECL_TEMPLATE(T, sampler) \
|
||||
static void Mix_Hrtf_##T##_##sampler(ALsource *Source, ALCdevice *Device, \
|
||||
const ALvoid *srcdata, ALuint *DataPosInt, ALuint *DataPosFrac, \
|
||||
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) \
|
||||
{ \
|
||||
const ALuint NumChannels = Source->NumChannels; \
|
||||
const T *RESTRICT data = srcdata; \
|
||||
const ALint *RESTRICT DelayStep = Source->Params.HrtfDelayStep; \
|
||||
ALfloat (*RESTRICT DryBuffer)[MAXCHANNELS]; \
|
||||
ALfloat *RESTRICT ClickRemoval, *RESTRICT PendingClicks; \
|
||||
ALfloat (*RESTRICT CoeffStep)[2] = Source->Params.HrtfCoeffStep; \
|
||||
ALuint pos, frac; \
|
||||
FILTER *DryFilter; \
|
||||
ALuint BufferIdx; \
|
||||
ALuint increment; \
|
||||
ALuint i, out, c; \
|
||||
ALfloat value; \
|
||||
\
|
||||
increment = Source->Params.Step; \
|
||||
\
|
||||
DryBuffer = Device->DryBuffer; \
|
||||
ClickRemoval = Device->ClickRemoval; \
|
||||
PendingClicks = Device->PendingClicks; \
|
||||
DryFilter = &Source->Params.iirFilter; \
|
||||
\
|
||||
pos = 0; \
|
||||
frac = *DataPosFrac; \
|
||||
\
|
||||
for(i = 0;i < NumChannels;i++) \
|
||||
{ \
|
||||
ALfloat (*RESTRICT TargetCoeffs)[2] = Source->Params.HrtfCoeffs[i]; \
|
||||
ALuint *RESTRICT TargetDelay = Source->Params.HrtfDelay[i]; \
|
||||
ALfloat *RESTRICT History = Source->HrtfHistory[i]; \
|
||||
ALfloat (*RESTRICT Values)[2] = Source->HrtfValues[i]; \
|
||||
ALint Counter = maxu(Source->HrtfCounter, OutPos) - OutPos; \
|
||||
ALuint Offset = Source->HrtfOffset + OutPos; \
|
||||
ALfloat Coeffs[HRIR_LENGTH][2]; \
|
||||
ALuint Delay[2]; \
|
||||
ALfloat left, right; \
|
||||
\
|
||||
pos = 0; \
|
||||
frac = *DataPosFrac; \
|
||||
\
|
||||
for(c = 0;c < HRIR_LENGTH;c++) \
|
||||
{ \
|
||||
Coeffs[c][0] = TargetCoeffs[c][0] - (CoeffStep[c][0]*Counter); \
|
||||
Coeffs[c][1] = TargetCoeffs[c][1] - (CoeffStep[c][1]*Counter); \
|
||||
} \
|
||||
\
|
||||
Delay[0] = TargetDelay[0] - (DelayStep[0]*Counter) + 32768; \
|
||||
Delay[1] = TargetDelay[1] - (DelayStep[1]*Counter) + 32768; \
|
||||
\
|
||||
if(LIKELY(OutPos == 0)) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
value = lpFilter2PC(DryFilter, i, value); \
|
||||
\
|
||||
History[Offset&SRC_HISTORY_MASK] = value; \
|
||||
left = History[(Offset-(Delay[0]>>16))&SRC_HISTORY_MASK]; \
|
||||
right = History[(Offset-(Delay[1]>>16))&SRC_HISTORY_MASK]; \
|
||||
\
|
||||
ClickRemoval[FRONT_LEFT] -= Values[(Offset+1)&HRIR_MASK][0] + \
|
||||
Coeffs[0][0] * left; \
|
||||
ClickRemoval[FRONT_RIGHT] -= Values[(Offset+1)&HRIR_MASK][1] + \
|
||||
Coeffs[0][1] * right; \
|
||||
} \
|
||||
for(BufferIdx = 0;BufferIdx < BufferSize && Counter > 0;BufferIdx++) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
value = lpFilter2P(DryFilter, i, value); \
|
||||
\
|
||||
History[Offset&SRC_HISTORY_MASK] = value; \
|
||||
left = History[(Offset-(Delay[0]>>16))&SRC_HISTORY_MASK]; \
|
||||
right = History[(Offset-(Delay[1]>>16))&SRC_HISTORY_MASK]; \
|
||||
\
|
||||
Delay[0] += DelayStep[0]; \
|
||||
Delay[1] += DelayStep[1]; \
|
||||
\
|
||||
Values[Offset&HRIR_MASK][0] = 0.0f; \
|
||||
Values[Offset&HRIR_MASK][1] = 0.0f; \
|
||||
Offset++; \
|
||||
\
|
||||
for(c = 0;c < HRIR_LENGTH;c++) \
|
||||
{ \
|
||||
const ALuint off = (Offset+c)&HRIR_MASK; \
|
||||
Values[off][0] += Coeffs[c][0] * left; \
|
||||
Values[off][1] += Coeffs[c][1] * right; \
|
||||
Coeffs[c][0] += CoeffStep[c][0]; \
|
||||
Coeffs[c][1] += CoeffStep[c][1]; \
|
||||
} \
|
||||
\
|
||||
DryBuffer[OutPos][FRONT_LEFT] += Values[Offset&HRIR_MASK][0]; \
|
||||
DryBuffer[OutPos][FRONT_RIGHT] += Values[Offset&HRIR_MASK][1]; \
|
||||
\
|
||||
frac += increment; \
|
||||
pos += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
OutPos++; \
|
||||
Counter--; \
|
||||
} \
|
||||
\
|
||||
Delay[0] >>= 16; \
|
||||
Delay[1] >>= 16; \
|
||||
for(;BufferIdx < BufferSize;BufferIdx++) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
value = lpFilter2P(DryFilter, i, value); \
|
||||
\
|
||||
History[Offset&SRC_HISTORY_MASK] = value; \
|
||||
left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; \
|
||||
right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; \
|
||||
\
|
||||
Values[Offset&HRIR_MASK][0] = 0.0f; \
|
||||
Values[Offset&HRIR_MASK][1] = 0.0f; \
|
||||
Offset++; \
|
||||
\
|
||||
ApplyCoeffs(Offset, Values, Coeffs, left, right); \
|
||||
DryBuffer[OutPos][FRONT_LEFT] += Values[Offset&HRIR_MASK][0]; \
|
||||
DryBuffer[OutPos][FRONT_RIGHT] += Values[Offset&HRIR_MASK][1]; \
|
||||
\
|
||||
frac += increment; \
|
||||
pos += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
OutPos++; \
|
||||
} \
|
||||
if(LIKELY(OutPos == SamplesToDo)) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
value = lpFilter2PC(DryFilter, i, value); \
|
||||
\
|
||||
History[Offset&SRC_HISTORY_MASK] = value; \
|
||||
left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; \
|
||||
right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; \
|
||||
\
|
||||
PendingClicks[FRONT_LEFT] += Values[(Offset+1)&HRIR_MASK][0] + \
|
||||
Coeffs[0][0] * left; \
|
||||
PendingClicks[FRONT_RIGHT] += Values[(Offset+1)&HRIR_MASK][1] + \
|
||||
Coeffs[0][1] * right; \
|
||||
} \
|
||||
OutPos -= BufferSize; \
|
||||
} \
|
||||
\
|
||||
for(out = 0;out < Device->NumAuxSends;out++) \
|
||||
{ \
|
||||
ALeffectslot *Slot = Source->Params.Send[out].Slot; \
|
||||
ALfloat WetSend; \
|
||||
ALfloat *RESTRICT WetBuffer; \
|
||||
ALfloat *RESTRICT WetClickRemoval; \
|
||||
ALfloat *RESTRICT WetPendingClicks; \
|
||||
FILTER *WetFilter; \
|
||||
\
|
||||
if(Slot == NULL) \
|
||||
continue; \
|
||||
\
|
||||
WetBuffer = Slot->WetBuffer; \
|
||||
WetClickRemoval = Slot->ClickRemoval; \
|
||||
WetPendingClicks = Slot->PendingClicks; \
|
||||
WetFilter = &Source->Params.Send[out].iirFilter; \
|
||||
WetSend = Source->Params.Send[out].WetGain; \
|
||||
\
|
||||
for(i = 0;i < NumChannels;i++) \
|
||||
{ \
|
||||
pos = 0; \
|
||||
frac = *DataPosFrac; \
|
||||
\
|
||||
if(LIKELY(OutPos == 0)) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels,frac);\
|
||||
value = lpFilter1PC(WetFilter, i, value); \
|
||||
\
|
||||
WetClickRemoval[0] -= value * WetSend; \
|
||||
} \
|
||||
for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels,frac);\
|
||||
value = lpFilter1P(WetFilter, i, value); \
|
||||
\
|
||||
WetBuffer[OutPos] += value * WetSend; \
|
||||
\
|
||||
frac += increment; \
|
||||
pos += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
OutPos++; \
|
||||
} \
|
||||
if(LIKELY(OutPos == SamplesToDo)) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels,frac);\
|
||||
value = lpFilter1PC(WetFilter, i, value); \
|
||||
\
|
||||
WetPendingClicks[0] += value * WetSend; \
|
||||
} \
|
||||
OutPos -= BufferSize; \
|
||||
} \
|
||||
} \
|
||||
*DataPosInt += pos; \
|
||||
*DataPosFrac = frac; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALfloat, point32)
|
||||
DECL_TEMPLATE(ALfloat, lerp32)
|
||||
DECL_TEMPLATE(ALfloat, cubic32)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
#define DECL_TEMPLATE(T, sampler) \
|
||||
static void Mix_##T##_##sampler(ALsource *Source, ALCdevice *Device, \
|
||||
const ALvoid *srcdata, ALuint *DataPosInt, ALuint *DataPosFrac, \
|
||||
ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) \
|
||||
{ \
|
||||
const ALuint NumChannels = Source->NumChannels; \
|
||||
const T *RESTRICT data = srcdata; \
|
||||
ALfloat (*RESTRICT DryBuffer)[MAXCHANNELS]; \
|
||||
ALfloat *RESTRICT ClickRemoval, *RESTRICT PendingClicks; \
|
||||
ALfloat DrySend[MAXCHANNELS]; \
|
||||
FILTER *DryFilter; \
|
||||
ALuint pos, frac; \
|
||||
ALuint BufferIdx; \
|
||||
ALuint increment; \
|
||||
ALuint i, out, c; \
|
||||
ALfloat value; \
|
||||
\
|
||||
increment = Source->Params.Step; \
|
||||
\
|
||||
DryBuffer = Device->DryBuffer; \
|
||||
ClickRemoval = Device->ClickRemoval; \
|
||||
PendingClicks = Device->PendingClicks; \
|
||||
DryFilter = &Source->Params.iirFilter; \
|
||||
\
|
||||
pos = 0; \
|
||||
frac = *DataPosFrac; \
|
||||
\
|
||||
for(i = 0;i < NumChannels;i++) \
|
||||
{ \
|
||||
for(c = 0;c < MAXCHANNELS;c++) \
|
||||
DrySend[c] = Source->Params.DryGains[i][c]; \
|
||||
\
|
||||
pos = 0; \
|
||||
frac = *DataPosFrac; \
|
||||
\
|
||||
if(OutPos == 0) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
\
|
||||
value = lpFilter2PC(DryFilter, i, value); \
|
||||
for(c = 0;c < MAXCHANNELS;c++) \
|
||||
ClickRemoval[c] -= value*DrySend[c]; \
|
||||
} \
|
||||
for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
\
|
||||
value = lpFilter2P(DryFilter, i, value); \
|
||||
for(c = 0;c < MAXCHANNELS;c++) \
|
||||
DryBuffer[OutPos][c] += value*DrySend[c]; \
|
||||
\
|
||||
frac += increment; \
|
||||
pos += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
OutPos++; \
|
||||
} \
|
||||
if(OutPos == SamplesToDo) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels, frac); \
|
||||
\
|
||||
value = lpFilter2PC(DryFilter, i, value); \
|
||||
for(c = 0;c < MAXCHANNELS;c++) \
|
||||
PendingClicks[c] += value*DrySend[c]; \
|
||||
} \
|
||||
OutPos -= BufferSize; \
|
||||
} \
|
||||
\
|
||||
for(out = 0;out < Device->NumAuxSends;out++) \
|
||||
{ \
|
||||
ALeffectslot *Slot = Source->Params.Send[out].Slot; \
|
||||
ALfloat WetSend; \
|
||||
ALfloat *WetBuffer; \
|
||||
ALfloat *WetClickRemoval; \
|
||||
ALfloat *WetPendingClicks; \
|
||||
FILTER *WetFilter; \
|
||||
\
|
||||
if(Slot == NULL) \
|
||||
continue; \
|
||||
\
|
||||
WetBuffer = Slot->WetBuffer; \
|
||||
WetClickRemoval = Slot->ClickRemoval; \
|
||||
WetPendingClicks = Slot->PendingClicks; \
|
||||
WetFilter = &Source->Params.Send[out].iirFilter; \
|
||||
WetSend = Source->Params.Send[out].WetGain; \
|
||||
\
|
||||
for(i = 0;i < NumChannels;i++) \
|
||||
{ \
|
||||
pos = 0; \
|
||||
frac = *DataPosFrac; \
|
||||
\
|
||||
if(OutPos == 0) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels,frac);\
|
||||
\
|
||||
value = lpFilter1PC(WetFilter, i, value); \
|
||||
WetClickRemoval[0] -= value * WetSend; \
|
||||
} \
|
||||
for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels,frac);\
|
||||
\
|
||||
value = lpFilter1P(WetFilter, i, value); \
|
||||
WetBuffer[OutPos] += value * WetSend; \
|
||||
\
|
||||
frac += increment; \
|
||||
pos += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
OutPos++; \
|
||||
} \
|
||||
if(OutPos == SamplesToDo) \
|
||||
{ \
|
||||
value = sampler(data + pos*NumChannels + i, NumChannels,frac);\
|
||||
\
|
||||
value = lpFilter1PC(WetFilter, i, value); \
|
||||
WetPendingClicks[0] += value * WetSend; \
|
||||
} \
|
||||
OutPos -= BufferSize; \
|
||||
} \
|
||||
} \
|
||||
*DataPosInt += pos; \
|
||||
*DataPosFrac = frac; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALfloat, point32)
|
||||
DECL_TEMPLATE(ALfloat, lerp32)
|
||||
DECL_TEMPLATE(ALfloat, cubic32)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
MixerFunc SelectMixer(enum Resampler Resampler)
|
||||
{
|
||||
switch(Resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Mix_ALfloat_point32;
|
||||
case LinearResampler:
|
||||
return Mix_ALfloat_lerp32;
|
||||
case CubicResampler:
|
||||
return Mix_ALfloat_cubic32;
|
||||
case ResamplerMax:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MixerFunc SelectHrtfMixer(enum Resampler Resampler)
|
||||
{
|
||||
switch(Resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Mix_Hrtf_ALfloat_point32;
|
||||
case LinearResampler:
|
||||
return Mix_Hrtf_ALfloat_lerp32;
|
||||
case CubicResampler:
|
||||
return Mix_Hrtf_ALfloat_cubic32;
|
||||
case ResamplerMax:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static __inline ALfloat Sample_ALbyte(ALbyte val)
|
||||
{ return val * (1.0f/127.0f); }
|
||||
|
||||
static __inline ALfloat Sample_ALshort(ALshort val)
|
||||
{ return val * (1.0f/32767.0f); }
|
||||
|
||||
static __inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static void Load_##T(ALfloat *dst, const T *src, ALuint samples) \
|
||||
{ \
|
||||
ALuint i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i] = Sample_##T(src[i]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALbyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void LoadStack(ALfloat *dst, const ALvoid *src, enum FmtType srctype, ALuint samples)
|
||||
{
|
||||
switch(srctype)
|
||||
{
|
||||
case FmtByte:
|
||||
Load_ALbyte(dst, src, samples);
|
||||
break;
|
||||
case FmtShort:
|
||||
Load_ALshort(dst, src, samples);
|
||||
break;
|
||||
case FmtFloat:
|
||||
Load_ALfloat(dst, src, samples);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SilenceStack(ALfloat *dst, ALuint samples)
|
||||
{
|
||||
ALuint i;
|
||||
for(i = 0;i < samples;i++)
|
||||
dst[i] = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo)
|
||||
{
|
||||
ALbufferlistitem *BufferListItem;
|
||||
ALuint DataPosInt, DataPosFrac;
|
||||
ALuint BuffersPlayed;
|
||||
ALboolean Looping;
|
||||
ALuint increment;
|
||||
enum Resampler Resampler;
|
||||
ALenum State;
|
||||
ALuint OutPos;
|
||||
ALuint NumChannels;
|
||||
ALuint FrameSize;
|
||||
ALint64 DataSize64;
|
||||
ALuint i;
|
||||
|
||||
/* Get source info */
|
||||
State = Source->state;
|
||||
BuffersPlayed = Source->BuffersPlayed;
|
||||
DataPosInt = Source->position;
|
||||
DataPosFrac = Source->position_fraction;
|
||||
Looping = Source->bLooping;
|
||||
increment = Source->Params.Step;
|
||||
Resampler = Source->Resampler;
|
||||
NumChannels = Source->NumChannels;
|
||||
FrameSize = NumChannels * Source->SampleSize;
|
||||
|
||||
/* Get current buffer queue item */
|
||||
BufferListItem = Source->queue;
|
||||
for(i = 0;i < BuffersPlayed;i++)
|
||||
BufferListItem = BufferListItem->next;
|
||||
|
||||
OutPos = 0;
|
||||
do {
|
||||
const ALuint BufferPrePadding = ResamplerPrePadding[Resampler];
|
||||
const ALuint BufferPadding = ResamplerPadding[Resampler];
|
||||
ALfloat StackData[STACK_DATA_SIZE/sizeof(ALfloat)];
|
||||
ALfloat *SrcData = StackData;
|
||||
ALuint SrcDataSize = 0;
|
||||
ALuint BufferSize;
|
||||
|
||||
/* Figure out how many buffer bytes will be needed */
|
||||
DataSize64 = SamplesToDo-OutPos+1;
|
||||
DataSize64 *= increment;
|
||||
DataSize64 += DataPosFrac+FRACTIONMASK;
|
||||
DataSize64 >>= FRACTIONBITS;
|
||||
DataSize64 += BufferPadding+BufferPrePadding;
|
||||
DataSize64 *= NumChannels;
|
||||
|
||||
BufferSize = (ALuint)mini64(DataSize64, STACK_DATA_SIZE/sizeof(ALfloat));
|
||||
BufferSize /= NumChannels;
|
||||
|
||||
if(Source->lSourceType == AL_STATIC)
|
||||
{
|
||||
const ALbuffer *ALBuffer = Source->queue->buffer;
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALuint DataSize;
|
||||
ALuint pos;
|
||||
|
||||
/* If current pos is beyond the loop range, do not loop */
|
||||
if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd)
|
||||
{
|
||||
Looping = AL_FALSE;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(BufferSize, DataSize);
|
||||
|
||||
SilenceStack(&SrcData[SrcDataSize*NumChannels],
|
||||
DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left to play in the source buffer, and clear the
|
||||
* rest of the temp buffer */
|
||||
DataSize = ALBuffer->SampleLen - pos;
|
||||
DataSize = minu(BufferSize, DataSize);
|
||||
|
||||
LoadStack(&SrcData[SrcDataSize*NumChannels], &Data[pos*FrameSize],
|
||||
ALBuffer->FmtType, DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
|
||||
SilenceStack(&SrcData[SrcDataSize*NumChannels],
|
||||
BufferSize*NumChannels);
|
||||
SrcDataSize += BufferSize;
|
||||
BufferSize -= BufferSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALuint LoopStart = ALBuffer->LoopStart;
|
||||
ALuint LoopEnd = ALBuffer->LoopEnd;
|
||||
|
||||
if(DataPosInt >= LoopStart)
|
||||
{
|
||||
pos = DataPosInt-LoopStart;
|
||||
while(pos < BufferPrePadding)
|
||||
pos += LoopEnd-LoopStart;
|
||||
pos -= BufferPrePadding;
|
||||
pos += LoopStart;
|
||||
}
|
||||
else if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
DataSize = BufferPrePadding - DataPosInt;
|
||||
DataSize = minu(BufferSize, DataSize);
|
||||
|
||||
SilenceStack(&SrcData[SrcDataSize*NumChannels], DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
/* Copy what's left of this loop iteration, then copy repeats
|
||||
* of the loop section */
|
||||
DataSize = LoopEnd - pos;
|
||||
DataSize = minu(BufferSize, DataSize);
|
||||
|
||||
LoadStack(&SrcData[SrcDataSize*NumChannels], &Data[pos*FrameSize],
|
||||
ALBuffer->FmtType, DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
|
||||
DataSize = LoopEnd-LoopStart;
|
||||
while(BufferSize > 0)
|
||||
{
|
||||
DataSize = minu(BufferSize, DataSize);
|
||||
|
||||
LoadStack(&SrcData[SrcDataSize*NumChannels], &Data[LoopStart*FrameSize],
|
||||
ALBuffer->FmtType, DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Crawl the buffer queue to fill in the temp buffer */
|
||||
ALbufferlistitem *tmpiter = BufferListItem;
|
||||
ALuint pos;
|
||||
|
||||
if(DataPosInt >= BufferPrePadding)
|
||||
pos = DataPosInt - BufferPrePadding;
|
||||
else
|
||||
{
|
||||
pos = BufferPrePadding - DataPosInt;
|
||||
while(pos > 0)
|
||||
{
|
||||
if(!tmpiter->prev && !Looping)
|
||||
{
|
||||
ALuint DataSize = minu(BufferSize, pos);
|
||||
|
||||
SilenceStack(&SrcData[SrcDataSize*NumChannels], DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
|
||||
pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(tmpiter->prev)
|
||||
tmpiter = tmpiter->prev;
|
||||
else
|
||||
{
|
||||
while(tmpiter->next)
|
||||
tmpiter = tmpiter->next;
|
||||
}
|
||||
|
||||
if(tmpiter->buffer)
|
||||
{
|
||||
if((ALuint)tmpiter->buffer->SampleLen > pos)
|
||||
{
|
||||
pos = tmpiter->buffer->SampleLen - pos;
|
||||
break;
|
||||
}
|
||||
pos -= tmpiter->buffer->SampleLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(tmpiter && BufferSize > 0)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
if((ALBuffer=tmpiter->buffer) != NULL)
|
||||
{
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
ALuint DataSize = ALBuffer->SampleLen;
|
||||
|
||||
/* Skip the data already played */
|
||||
if(DataSize <= pos)
|
||||
pos -= DataSize;
|
||||
else
|
||||
{
|
||||
Data += pos*FrameSize;
|
||||
DataSize -= pos;
|
||||
pos -= pos;
|
||||
|
||||
DataSize = minu(BufferSize, DataSize);
|
||||
LoadStack(&SrcData[SrcDataSize*NumChannels], Data,
|
||||
ALBuffer->FmtType, DataSize*NumChannels);
|
||||
SrcDataSize += DataSize;
|
||||
BufferSize -= DataSize;
|
||||
}
|
||||
}
|
||||
tmpiter = tmpiter->next;
|
||||
if(!tmpiter && Looping)
|
||||
tmpiter = Source->queue;
|
||||
else if(!tmpiter)
|
||||
{
|
||||
SilenceStack(&SrcData[SrcDataSize*NumChannels], BufferSize*NumChannels);
|
||||
SrcDataSize += BufferSize;
|
||||
BufferSize -= BufferSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out how many samples we can mix. */
|
||||
DataSize64 = SrcDataSize;
|
||||
DataSize64 -= BufferPadding+BufferPrePadding;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= increment;
|
||||
DataSize64 -= DataPosFrac;
|
||||
|
||||
BufferSize = (ALuint)((DataSize64+(increment-1)) / increment);
|
||||
BufferSize = minu(BufferSize, (SamplesToDo-OutPos));
|
||||
|
||||
SrcData += BufferPrePadding*NumChannels;
|
||||
Source->Params.DoMix(Source, Device, SrcData, &DataPosInt, &DataPosFrac,
|
||||
OutPos, SamplesToDo, BufferSize);
|
||||
OutPos += BufferSize;
|
||||
|
||||
/* Handle looping sources */
|
||||
while(1)
|
||||
{
|
||||
const ALbuffer *ALBuffer;
|
||||
ALuint DataSize = 0;
|
||||
ALuint LoopStart = 0;
|
||||
ALuint LoopEnd = 0;
|
||||
|
||||
if((ALBuffer=BufferListItem->buffer) != NULL)
|
||||
{
|
||||
DataSize = ALBuffer->SampleLen;
|
||||
LoopStart = ALBuffer->LoopStart;
|
||||
LoopEnd = ALBuffer->LoopEnd;
|
||||
if(LoopEnd > DataPosInt)
|
||||
break;
|
||||
}
|
||||
|
||||
if(Looping && Source->lSourceType == AL_STATIC)
|
||||
{
|
||||
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
|
||||
break;
|
||||
}
|
||||
|
||||
if(DataSize > DataPosInt)
|
||||
break;
|
||||
|
||||
if(BufferListItem->next)
|
||||
{
|
||||
BufferListItem = BufferListItem->next;
|
||||
BuffersPlayed++;
|
||||
}
|
||||
else if(Looping)
|
||||
{
|
||||
BufferListItem = Source->queue;
|
||||
BuffersPlayed = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
State = AL_STOPPED;
|
||||
BufferListItem = Source->queue;
|
||||
BuffersPlayed = Source->BuffersInQueue;
|
||||
DataPosInt = 0;
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
DataPosInt -= DataSize;
|
||||
}
|
||||
} while(State == AL_PLAYING && OutPos < SamplesToDo);
|
||||
|
||||
/* Update source info */
|
||||
Source->state = State;
|
||||
Source->BuffersPlayed = BuffersPlayed;
|
||||
Source->position = DataPosInt;
|
||||
Source->position_fraction = DataPosFrac;
|
||||
Source->HrtfOffset += OutPos;
|
||||
if(State == AL_PLAYING)
|
||||
{
|
||||
Source->HrtfCounter = maxu(Source->HrtfCounter, OutPos) - OutPos;
|
||||
Source->HrtfMoving = AL_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Source->HrtfCounter = 0;
|
||||
Source->HrtfMoving = AL_FALSE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#ifndef MIXER_DEFS_H
|
||||
#define MIXER_DEFS_H
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
struct MixGains;
|
||||
|
||||
struct MixHrtfParams;
|
||||
struct HrtfState;
|
||||
|
||||
/* C resamplers */
|
||||
const ALfloat *Resample_copy_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_point_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_lerp_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_cubic_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
const ALfloat *Resample_bsinc_C(const InterpState *state, const ALfloat *restrict src, ALsizei frac, ALint increment, ALfloat *restrict dst, ALsizei dstlen);
|
||||
|
||||
|
||||
/* C mixers */
|
||||
void MixHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
|
||||
struct HrtfState *hrtfstate, ALsizei BufferSize);
|
||||
void MixHrtfBlend_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, const HrtfParams *oldparams,
|
||||
MixHrtfParams *newparams, HrtfState *hrtfstate,
|
||||
ALsizei BufferSize);
|
||||
void MixDirectHrtf_C(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
|
||||
ALsizei BufferSize);
|
||||
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize);
|
||||
void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains,
|
||||
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
|
||||
ALsizei InPos, ALsizei BufferSize);
|
||||
|
||||
/* SSE mixers */
|
||||
void MixHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
|
||||
struct HrtfState *hrtfstate, ALsizei BufferSize);
|
||||
void MixHrtfBlend_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, const HrtfParams *oldparams,
|
||||
MixHrtfParams *newparams, HrtfState *hrtfstate,
|
||||
ALsizei BufferSize);
|
||||
void MixDirectHrtf_SSE(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
|
||||
ALsizei BufferSize);
|
||||
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize);
|
||||
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains,
|
||||
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
|
||||
ALsizei InPos, ALsizei BufferSize);
|
||||
|
||||
/* SSE resamplers */
|
||||
inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size)
|
||||
{
|
||||
ALsizei i;
|
||||
|
||||
pos_arr[0] = 0;
|
||||
frac_arr[0] = frac;
|
||||
for(i = 1;i < size;i++)
|
||||
{
|
||||
ALint frac_tmp = frac_arr[i-1] + increment;
|
||||
pos_arr[i] = pos_arr[i-1] + (frac_tmp>>FRACTIONBITS);
|
||||
frac_arr[i] = frac_tmp&FRACTIONMASK;
|
||||
}
|
||||
}
|
||||
|
||||
const ALfloat *Resample_lerp_SSE2(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_lerp_SSE41(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
|
||||
const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen);
|
||||
|
||||
/* Neon mixers */
|
||||
void MixHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, struct MixHrtfParams *hrtfparams,
|
||||
struct HrtfState *hrtfstate, ALsizei BufferSize);
|
||||
void MixHrtfBlend_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, const HrtfParams *oldparams,
|
||||
MixHrtfParams *newparams, HrtfState *hrtfstate,
|
||||
ALsizei BufferSize);
|
||||
void MixDirectHrtf_Neon(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
|
||||
ALsizei BufferSize);
|
||||
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize);
|
||||
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains,
|
||||
const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans,
|
||||
ALsizei InPos, ALsizei BufferSize);
|
||||
|
||||
/* Neon resamplers */
|
||||
const ALfloat *Resample_lerp_Neon(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei numsamples);
|
||||
const ALfloat *Resample_bsinc_Neon(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen);
|
||||
|
||||
#endif /* MIXER_DEFS_H */
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alSource.h"
|
||||
|
||||
#include "hrtf.h"
|
||||
#include "align.h"
|
||||
#include "alu.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei irSize,
|
||||
const ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right);
|
||||
|
||||
|
||||
void MixHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, MixHrtfParams *hrtfparams, HrtfState *hrtfstate,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat (*Coeffs)[2] = ASSUME_ALIGNED(hrtfparams->Coeffs, 16);
|
||||
const ALsizei Delay[2] = { hrtfparams->Delay[0], hrtfparams->Delay[1] };
|
||||
const ALfloat gainstep = hrtfparams->GainStep;
|
||||
const ALfloat gain = hrtfparams->Gain;
|
||||
ALfloat g, stepcount = 0.0f;
|
||||
ALfloat left, right;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(IrSize >= 4);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
LeftOut += OutPos;
|
||||
RightOut += OutPos;
|
||||
for(i = 0;i < BufferSize;i++)
|
||||
{
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
|
||||
|
||||
g = gain + gainstep*stepcount;
|
||||
left = hrtfstate->History[(Offset-Delay[0])&HRTF_HISTORY_MASK]*g;
|
||||
right = hrtfstate->History[(Offset-Delay[1])&HRTF_HISTORY_MASK]*g;
|
||||
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
|
||||
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, Coeffs, left, right);
|
||||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
stepcount += 1.0f;
|
||||
Offset++;
|
||||
}
|
||||
hrtfparams->Gain = gain + gainstep*stepcount;
|
||||
}
|
||||
|
||||
void MixHrtfBlend(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, ALsizei OutPos,
|
||||
const ALsizei IrSize, const HrtfParams *oldparams,
|
||||
MixHrtfParams *newparams, HrtfState *hrtfstate,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat (*OldCoeffs)[2] = ASSUME_ALIGNED(oldparams->Coeffs, 16);
|
||||
const ALsizei OldDelay[2] = { oldparams->Delay[0], oldparams->Delay[1] };
|
||||
const ALfloat oldGain = oldparams->Gain;
|
||||
const ALfloat oldGainStep = -oldGain / (ALfloat)BufferSize;
|
||||
const ALfloat (*NewCoeffs)[2] = ASSUME_ALIGNED(newparams->Coeffs, 16);
|
||||
const ALsizei NewDelay[2] = { newparams->Delay[0], newparams->Delay[1] };
|
||||
const ALfloat newGain = newparams->Gain;
|
||||
const ALfloat newGainStep = newparams->GainStep;
|
||||
ALfloat g, stepcount = 0.0f;
|
||||
ALfloat left, right;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(IrSize >= 4);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
LeftOut += OutPos;
|
||||
RightOut += OutPos;
|
||||
for(i = 0;i < BufferSize;i++)
|
||||
{
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][0] = 0.0f;
|
||||
hrtfstate->Values[(Offset+IrSize-1)&HRIR_MASK][1] = 0.0f;
|
||||
|
||||
hrtfstate->History[Offset&HRTF_HISTORY_MASK] = *(data++);
|
||||
|
||||
g = oldGain + oldGainStep*stepcount;
|
||||
left = hrtfstate->History[(Offset-OldDelay[0])&HRTF_HISTORY_MASK]*g;
|
||||
right = hrtfstate->History[(Offset-OldDelay[1])&HRTF_HISTORY_MASK]*g;
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, OldCoeffs, left, right);
|
||||
|
||||
g = newGain + newGainStep*stepcount;
|
||||
left = hrtfstate->History[(Offset-NewDelay[0])&HRTF_HISTORY_MASK]*g;
|
||||
right = hrtfstate->History[(Offset-NewDelay[1])&HRTF_HISTORY_MASK]*g;
|
||||
ApplyCoeffs(Offset, hrtfstate->Values, IrSize, NewCoeffs, left, right);
|
||||
|
||||
*(LeftOut++) += hrtfstate->Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += hrtfstate->Values[Offset&HRIR_MASK][1];
|
||||
|
||||
stepcount += 1.0f;
|
||||
Offset++;
|
||||
}
|
||||
newparams->Gain = newGain + newGainStep*stepcount;
|
||||
}
|
||||
|
||||
void MixDirectHrtf(ALfloat *restrict LeftOut, ALfloat *restrict RightOut,
|
||||
const ALfloat *data, ALsizei Offset, const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2], ALfloat (*restrict Values)[2],
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
ALfloat insample;
|
||||
ALsizei i;
|
||||
|
||||
ASSUME(IrSize >= 4);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(i = 0;i < BufferSize;i++)
|
||||
{
|
||||
Values[(Offset+IrSize)&HRIR_MASK][0] = 0.0f;
|
||||
Values[(Offset+IrSize)&HRIR_MASK][1] = 0.0f;
|
||||
Offset++;
|
||||
|
||||
insample = *(data++);
|
||||
ApplyCoeffs(Offset, Values, IrSize, Coeffs, insample, insample);
|
||||
*(LeftOut++) += Values[Offset&HRIR_MASK][0];
|
||||
*(RightOut++) += Values[Offset&HRIR_MASK][1];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
static inline ALfloat do_point(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei UNUSED(frac))
|
||||
{ return vals[0]; }
|
||||
static inline ALfloat do_lerp(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return lerp(vals[0], vals[1], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat do_cubic(const InterpState* UNUSED(state), const ALfloat *restrict vals, ALsizei frac)
|
||||
{ return cubic(vals[0], vals[1], vals[2], vals[3], frac * (1.0f/FRACTIONONE)); }
|
||||
static inline ALfloat do_bsinc(const InterpState *state, const ALfloat *restrict vals, ALsizei frac)
|
||||
{
|
||||
const ALfloat *fil, *scd, *phd, *spd;
|
||||
ALsizei j_f, pi;
|
||||
ALfloat pf, r;
|
||||
|
||||
ASSUME(state->bsinc.m > 0);
|
||||
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
fil = ASSUME_ALIGNED(state->bsinc.filter + state->bsinc.m*pi*4, 16);
|
||||
scd = ASSUME_ALIGNED(fil + state->bsinc.m, 16);
|
||||
phd = ASSUME_ALIGNED(scd + state->bsinc.m, 16);
|
||||
spd = ASSUME_ALIGNED(phd + state->bsinc.m, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r = 0.0f;
|
||||
for(j_f = 0;j_f < state->bsinc.m;j_f++)
|
||||
r += (fil[j_f] + state->bsinc.sf*scd[j_f] + pf*(phd[j_f] + state->bsinc.sf*spd[j_f])) * vals[j_f];
|
||||
return r;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_copy_C(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei UNUSED(frac), ALint UNUSED(increment),
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
#if defined(HAVE_SSE) || defined(HAVE_NEON)
|
||||
/* Avoid copying the source data if it's aligned like the destination. */
|
||||
if((((intptr_t)src)&15) == (((intptr_t)dst)&15))
|
||||
return src;
|
||||
#endif
|
||||
memcpy(dst, src, numsamples*sizeof(ALfloat));
|
||||
return dst;
|
||||
}
|
||||
|
||||
#define DECL_TEMPLATE(Tag, Sampler, O) \
|
||||
const ALfloat *Resample_##Tag##_C(const InterpState *state, \
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment, \
|
||||
ALfloat *restrict dst, ALsizei numsamples) \
|
||||
{ \
|
||||
const InterpState istate = *state; \
|
||||
ALsizei i; \
|
||||
\
|
||||
ASSUME(numsamples > 0); \
|
||||
\
|
||||
src -= O; \
|
||||
for(i = 0;i < numsamples;i++) \
|
||||
{ \
|
||||
dst[i] = Sampler(&istate, src, frac); \
|
||||
\
|
||||
frac += increment; \
|
||||
src += frac>>FRACTIONBITS; \
|
||||
frac &= FRACTIONMASK; \
|
||||
} \
|
||||
return dst; \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(point, do_point, 0)
|
||||
DECL_TEMPLATE(lerp, do_lerp, 0)
|
||||
DECL_TEMPLATE(cubic, do_cubic, 1)
|
||||
DECL_TEMPLATE(bsinc, do_bsinc, istate.bsinc.l)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALsizei c;
|
||||
for(c = 0;c < IrSize;c++)
|
||||
{
|
||||
const ALsizei off = (Offset+c)&HRIR_MASK;
|
||||
Values[off][0] += Coeffs[c][0] * left;
|
||||
Values[off][1] += Coeffs[c][1] * right;
|
||||
}
|
||||
}
|
||||
|
||||
#define MixHrtf MixHrtf_C
|
||||
#define MixHrtfBlend MixHrtfBlend_C
|
||||
#define MixDirectHrtf MixDirectHrtf_C
|
||||
#include "hrtf_inc.c"
|
||||
|
||||
|
||||
void Mix_C(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos] * (gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGains[c];
|
||||
else
|
||||
gain += step*step_count;
|
||||
CurrentGains[c] = gain;
|
||||
}
|
||||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
|
||||
/* Basically the inverse of the above. Rather than one input going to multiple
|
||||
* outputs (each with its own gain), it's multiple inputs (each with its own
|
||||
* gain) going to one output. This applies one row (vs one column) of a matrix
|
||||
* transform. And as the matrices are more or less static once set up, no
|
||||
* stepping is necessary.
|
||||
*/
|
||||
void MixRow_C(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
ALsizei c, i;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
for(i = 0;i < BufferSize;i++)
|
||||
OutBuffer[i] += data[c][InPos+i] * gain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "hrtf.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp_Neon(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
const int32x4_t increment4 = vdupq_n_s32(increment*4);
|
||||
const float32x4_t fracOne4 = vdupq_n_f32(1.0f/FRACTIONONE);
|
||||
const int32x4_t fracMask4 = vdupq_n_s32(FRACTIONMASK);
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
int32x4_t pos4, frac4;
|
||||
ALsizei todo, pos, i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
frac4 = vld1q_s32(frac_);
|
||||
pos4 = vld1q_s32(pos_);
|
||||
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
{
|
||||
const int pos0 = vgetq_lane_s32(pos4, 0);
|
||||
const int pos1 = vgetq_lane_s32(pos4, 1);
|
||||
const int pos2 = vgetq_lane_s32(pos4, 2);
|
||||
const int pos3 = vgetq_lane_s32(pos4, 3);
|
||||
const float32x4_t val1 = (float32x4_t){src[pos0], src[pos1], src[pos2], src[pos3]};
|
||||
const float32x4_t val2 = (float32x4_t){src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]};
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const float32x4_t r0 = vsubq_f32(val2, val1);
|
||||
const float32x4_t mu = vmulq_f32(vcvtq_f32_s32(frac4), fracOne4);
|
||||
const float32x4_t out = vmlaq_f32(val1, mu, r0);
|
||||
|
||||
vst1q_f32(&dst[i], out);
|
||||
|
||||
frac4 = vaddq_s32(frac4, increment4);
|
||||
pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, FRACTIONBITS));
|
||||
frac4 = vandq_s32(frac4, fracMask4);
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = vgetq_lane_s32(pos4, 0);
|
||||
frac = vgetq_lane_s32(frac4, 0);
|
||||
|
||||
for(;i < numsamples;++i)
|
||||
{
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
const ALfloat *Resample_bsinc_Neon(const InterpState *state,
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei dstlen)
|
||||
{
|
||||
const ALfloat *const filter = state->bsinc.filter;
|
||||
const float32x4_t sf4 = vdupq_n_f32(state->bsinc.sf);
|
||||
const ALsizei m = state->bsinc.m;
|
||||
const float32x4_t *fil, *scd, *phd, *spd;
|
||||
ALsizei pi, i, j, offset;
|
||||
float32x4_t r4;
|
||||
ALfloat pf;
|
||||
|
||||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src -= state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
offset = m*pi*4;
|
||||
fil = ASSUME_ALIGNED(filter + offset, 16); offset += m;
|
||||
scd = ASSUME_ALIGNED(filter + offset, 16); offset += m;
|
||||
phd = ASSUME_ALIGNED(filter + offset, 16); offset += m;
|
||||
spd = ASSUME_ALIGNED(filter + offset, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r4 = vdupq_n_f32(0.0f);
|
||||
{
|
||||
const ALsizei count = m >> 2;
|
||||
const float32x4_t pf4 = vdupq_n_f32(pf);
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
for(j = 0;j < count;j++)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const float32x4_t f4 = vmlaq_f32(
|
||||
vmlaq_f32(fil[j], sf4, scd[j]),
|
||||
pf4, vmlaq_f32(phd[j], sf4, spd[j])
|
||||
);
|
||||
/* r += f*src */
|
||||
r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j*4]));
|
||||
}
|
||||
}
|
||||
r4 = vaddq_f32(r4, vcombine_f32(vrev64_f32(vget_high_f32(r4)),
|
||||
vrev64_f32(vget_low_f32(r4))));
|
||||
dst[i] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
|
||||
frac += increment;
|
||||
src += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
ALsizei c;
|
||||
float32x4_t leftright4;
|
||||
{
|
||||
float32x2_t leftright2 = vdup_n_f32(0.0);
|
||||
leftright2 = vset_lane_f32(left, leftright2, 0);
|
||||
leftright2 = vset_lane_f32(right, leftright2, 1);
|
||||
leftright4 = vcombine_f32(leftright2, leftright2);
|
||||
}
|
||||
Values = ASSUME_ALIGNED(Values, 16);
|
||||
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
|
||||
for(c = 0;c < IrSize;c += 2)
|
||||
{
|
||||
const ALsizei o0 = (Offset+c)&HRIR_MASK;
|
||||
const ALsizei o1 = (o0+1)&HRIR_MASK;
|
||||
float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]),
|
||||
vld1_f32((float32_t*)&Values[o1][0]));
|
||||
float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]);
|
||||
|
||||
vals = vmlaq_f32(vals, coefs, leftright4);
|
||||
|
||||
vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals));
|
||||
vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals));
|
||||
}
|
||||
}
|
||||
|
||||
#define MixHrtf MixHrtf_Neon
|
||||
#define MixHrtfBlend MixHrtfBlend_Neon
|
||||
#define MixDirectHrtf MixDirectHrtf_Neon
|
||||
#include "hrtf_inc.c"
|
||||
|
||||
|
||||
void Mix_Neon(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
data = ASSUME_ALIGNED(data, 16);
|
||||
OutBuffer = ASSUME_ALIGNED(OutBuffer, 16);
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(LIKELY(minsize > 3))
|
||||
{
|
||||
const float32x4_t four4 = vdupq_n_f32(4.0f);
|
||||
const float32x4_t step4 = vdupq_n_f32(step);
|
||||
const float32x4_t gain4 = vdupq_n_f32(gain);
|
||||
float32x4_t step_count4 = vsetq_lane_f32(0.0f,
|
||||
vsetq_lane_f32(1.0f,
|
||||
vsetq_lane_f32(2.0f,
|
||||
vsetq_lane_f32(3.0f, vdupq_n_f32(0.0f), 3),
|
||||
2), 1), 0
|
||||
);
|
||||
ALsizei todo = minsize >> 2;
|
||||
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
|
||||
step_count4 = vaddq_f32(step_count4, four4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
/* NOTE: step_count4 now represents the next four counts after
|
||||
* the last four mixed samples, so the lowest element
|
||||
* represents the next step count to apply.
|
||||
*/
|
||||
step_count = vgetq_lane_f32(step_count4, 0);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGains[c];
|
||||
else
|
||||
gain += step*step_count;
|
||||
CurrentGains[c] = gain;
|
||||
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
minsize = mini(BufferSize, (pos+3)&~3);
|
||||
for(;pos < minsize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
{
|
||||
ALsizei todo = (BufferSize-pos) >> 2;
|
||||
const float32x4_t gain4 = vdupq_n_f32(gain);
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&data[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
|
||||
void MixRow_Neon(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
if(LIKELY(BufferSize > 3))
|
||||
{
|
||||
ALsizei todo = BufferSize >> 2;
|
||||
float32x4_t gain4 = vdupq_n_f32(gain);
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&data[c][InPos+pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&OutBuffer[pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&OutBuffer[pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[pos] += data[c][InPos+pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#include "alSource.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_bsinc_SSE(const InterpState *state, const ALfloat *restrict src,
|
||||
ALsizei frac, ALint increment, ALfloat *restrict dst,
|
||||
ALsizei dstlen)
|
||||
{
|
||||
const ALfloat *const filter = state->bsinc.filter;
|
||||
const __m128 sf4 = _mm_set1_ps(state->bsinc.sf);
|
||||
const ALsizei m = state->bsinc.m;
|
||||
const __m128 *fil, *scd, *phd, *spd;
|
||||
ALsizei pi, i, j, offset;
|
||||
ALfloat pf;
|
||||
__m128 r4;
|
||||
|
||||
ASSUME(m > 0);
|
||||
ASSUME(dstlen > 0);
|
||||
|
||||
src -= state->bsinc.l;
|
||||
for(i = 0;i < dstlen;i++)
|
||||
{
|
||||
// Calculate the phase index and factor.
|
||||
#define FRAC_PHASE_BITDIFF (FRACTIONBITS-BSINC_PHASE_BITS)
|
||||
pi = frac >> FRAC_PHASE_BITDIFF;
|
||||
pf = (frac & ((1<<FRAC_PHASE_BITDIFF)-1)) * (1.0f/(1<<FRAC_PHASE_BITDIFF));
|
||||
#undef FRAC_PHASE_BITDIFF
|
||||
|
||||
offset = m*pi*4;
|
||||
fil = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
|
||||
scd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
|
||||
phd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16); offset += m;
|
||||
spd = (const __m128*)ASSUME_ALIGNED(filter + offset, 16);
|
||||
|
||||
// Apply the scale and phase interpolated filter.
|
||||
r4 = _mm_setzero_ps();
|
||||
{
|
||||
const ALsizei count = m >> 2;
|
||||
const __m128 pf4 = _mm_set1_ps(pf);
|
||||
|
||||
ASSUME(count > 0);
|
||||
|
||||
#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
|
||||
for(j = 0;j < count;j++)
|
||||
{
|
||||
/* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
|
||||
const __m128 f4 = MLA4(
|
||||
MLA4(fil[j], sf4, scd[j]),
|
||||
pf4, MLA4(phd[j], sf4, spd[j])
|
||||
);
|
||||
/* r += f*src */
|
||||
r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j*4]));
|
||||
}
|
||||
#undef MLA4
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
dst[i] = _mm_cvtss_f32(r4);
|
||||
|
||||
frac += increment;
|
||||
src += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
static inline void ApplyCoeffs(ALsizei Offset, ALfloat (*restrict Values)[2],
|
||||
const ALsizei IrSize,
|
||||
const ALfloat (*restrict Coeffs)[2],
|
||||
ALfloat left, ALfloat right)
|
||||
{
|
||||
const __m128 lrlr = _mm_setr_ps(left, right, left, right);
|
||||
__m128 vals = _mm_setzero_ps();
|
||||
__m128 coeffs;
|
||||
ALsizei i;
|
||||
|
||||
Values = ASSUME_ALIGNED(Values, 16);
|
||||
Coeffs = ASSUME_ALIGNED(Coeffs, 16);
|
||||
if((Offset&1))
|
||||
{
|
||||
const ALsizei o0 = Offset&HRIR_MASK;
|
||||
const ALsizei o1 = (Offset+IrSize-1)&HRIR_MASK;
|
||||
__m128 imp0, imp1;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[0][0]);
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o0][0]);
|
||||
imp0 = _mm_mul_ps(lrlr, coeffs);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o0][0], vals);
|
||||
for(i = 1;i < IrSize-1;i += 2)
|
||||
{
|
||||
const ALsizei o2 = (Offset+i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i+1][0]);
|
||||
vals = _mm_load_ps(&Values[o2][0]);
|
||||
imp1 = _mm_mul_ps(lrlr, coeffs);
|
||||
imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_store_ps(&Values[o2][0], vals);
|
||||
imp0 = imp1;
|
||||
}
|
||||
vals = _mm_loadl_pi(vals, (__m64*)&Values[o1][0]);
|
||||
imp0 = _mm_movehl_ps(imp0, imp0);
|
||||
vals = _mm_add_ps(imp0, vals);
|
||||
_mm_storel_pi((__m64*)&Values[o1][0], vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i = 0;i < IrSize;i += 2)
|
||||
{
|
||||
const ALsizei o = (Offset + i)&HRIR_MASK;
|
||||
|
||||
coeffs = _mm_load_ps(&Coeffs[i][0]);
|
||||
vals = _mm_load_ps(&Values[o][0]);
|
||||
vals = _mm_add_ps(vals, _mm_mul_ps(lrlr, coeffs));
|
||||
_mm_store_ps(&Values[o][0], vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MixHrtf MixHrtf_SSE
|
||||
#define MixHrtfBlend MixHrtfBlend_SSE
|
||||
#define MixDirectHrtf MixDirectHrtf_SSE
|
||||
#include "hrtf_inc.c"
|
||||
|
||||
|
||||
void Mix_SSE(const ALfloat *data, ALsizei OutChans, ALfloat (*restrict OutBuffer)[BUFFERSIZE],
|
||||
ALfloat *CurrentGains, const ALfloat *TargetGains, ALsizei Counter, ALsizei OutPos,
|
||||
ALsizei BufferSize)
|
||||
{
|
||||
const ALfloat delta = (Counter > 0) ? 1.0f/(ALfloat)Counter : 0.0f;
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(OutChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < OutChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
ALfloat gain = CurrentGains[c];
|
||||
const ALfloat diff = TargetGains[c] - gain;
|
||||
|
||||
if(fabsf(diff) > FLT_EPSILON)
|
||||
{
|
||||
ALsizei minsize = mini(BufferSize, Counter);
|
||||
const ALfloat step = diff * delta;
|
||||
ALfloat step_count = 0.0f;
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(LIKELY(minsize > 3))
|
||||
{
|
||||
const __m128 four4 = _mm_set1_ps(4.0f);
|
||||
const __m128 step4 = _mm_set1_ps(step);
|
||||
const __m128 gain4 = _mm_set1_ps(gain);
|
||||
__m128 step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f);
|
||||
ALsizei todo = minsize >> 2;
|
||||
do {
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z))
|
||||
/* dry += val * (gain + step*step_count) */
|
||||
dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4));
|
||||
#undef MLA4
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
step_count4 = _mm_add_ps(step_count4, four4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
/* NOTE: step_count4 now represents the next four counts after
|
||||
* the last four mixed samples, so the lowest element
|
||||
* represents the next step count to apply.
|
||||
*/
|
||||
step_count = _mm_cvtss_f32(step_count4);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(;pos < minsize;pos++)
|
||||
{
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*(gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGains[c];
|
||||
else
|
||||
gain += step*step_count;
|
||||
CurrentGains[c] = gain;
|
||||
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
minsize = mini(BufferSize, (pos+3)&~3);
|
||||
for(;pos < minsize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
if(LIKELY(BufferSize-pos > 3))
|
||||
{
|
||||
ALsizei todo = (BufferSize-pos) >> 2;
|
||||
const __m128 gain4 = _mm_set1_ps(gain);
|
||||
do {
|
||||
const __m128 val4 = _mm_load_ps(&data[pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[c][OutPos+pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&OutBuffer[c][OutPos+pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[c][OutPos+pos] += data[pos]*gain;
|
||||
}
|
||||
}
|
||||
|
||||
void MixRow_SSE(ALfloat *OutBuffer, const ALfloat *Gains, const ALfloat (*restrict data)[BUFFERSIZE], ALsizei InChans, ALsizei InPos, ALsizei BufferSize)
|
||||
{
|
||||
ALsizei c;
|
||||
|
||||
ASSUME(InChans > 0);
|
||||
ASSUME(BufferSize > 0);
|
||||
|
||||
for(c = 0;c < InChans;c++)
|
||||
{
|
||||
ALsizei pos = 0;
|
||||
const ALfloat gain = Gains[c];
|
||||
if(!(fabsf(gain) > GAIN_SILENCE_THRESHOLD))
|
||||
continue;
|
||||
|
||||
if(LIKELY(BufferSize > 3))
|
||||
{
|
||||
ALsizei todo = BufferSize >> 2;
|
||||
const __m128 gain4 = _mm_set1_ps(gain);
|
||||
do {
|
||||
const __m128 val4 = _mm_load_ps(&data[c][InPos+pos]);
|
||||
__m128 dry4 = _mm_load_ps(&OutBuffer[pos]);
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&OutBuffer[pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
}
|
||||
for(;pos < BufferSize;pos++)
|
||||
OutBuffer[pos] += data[c][InPos+pos]*gain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp_SSE2(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALsizei todo, pos, i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
|
||||
pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
|
||||
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
{
|
||||
const int pos0 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(0, 0, 0, 0)));
|
||||
const int pos1 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
const int pos2 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(2, 2, 2, 2)));
|
||||
const int pos3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(pos4, _MM_SHUFFLE(3, 3, 3, 3)));
|
||||
const __m128 val1 = _mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = _mm_cvtsi128_si32(pos4);
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
for(;i < numsamples;++i)
|
||||
{
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 2014 by Timothy Arceri <t_arceri@yahoo.com.au>.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#include <smmintrin.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "defs.h"
|
||||
|
||||
|
||||
const ALfloat *Resample_lerp_SSE41(const InterpState* UNUSED(state),
|
||||
const ALfloat *restrict src, ALsizei frac, ALint increment,
|
||||
ALfloat *restrict dst, ALsizei numsamples)
|
||||
{
|
||||
const __m128i increment4 = _mm_set1_epi32(increment*4);
|
||||
const __m128 fracOne4 = _mm_set1_ps(1.0f/FRACTIONONE);
|
||||
const __m128i fracMask4 = _mm_set1_epi32(FRACTIONMASK);
|
||||
alignas(16) ALsizei pos_[4], frac_[4];
|
||||
__m128i frac4, pos4;
|
||||
ALsizei todo, pos, i;
|
||||
|
||||
ASSUME(numsamples > 0);
|
||||
|
||||
InitiatePositionArrays(frac, increment, frac_, pos_, 4);
|
||||
frac4 = _mm_setr_epi32(frac_[0], frac_[1], frac_[2], frac_[3]);
|
||||
pos4 = _mm_setr_epi32(pos_[0], pos_[1], pos_[2], pos_[3]);
|
||||
|
||||
todo = numsamples & ~3;
|
||||
for(i = 0;i < todo;i += 4)
|
||||
{
|
||||
const int pos0 = _mm_extract_epi32(pos4, 0);
|
||||
const int pos1 = _mm_extract_epi32(pos4, 1);
|
||||
const int pos2 = _mm_extract_epi32(pos4, 2);
|
||||
const int pos3 = _mm_extract_epi32(pos4, 3);
|
||||
const __m128 val1 = _mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ]);
|
||||
const __m128 val2 = _mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1]);
|
||||
|
||||
/* val1 + (val2-val1)*mu */
|
||||
const __m128 r0 = _mm_sub_ps(val2, val1);
|
||||
const __m128 mu = _mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4);
|
||||
const __m128 out = _mm_add_ps(val1, _mm_mul_ps(mu, r0));
|
||||
|
||||
_mm_store_ps(&dst[i], out);
|
||||
|
||||
frac4 = _mm_add_epi32(frac4, increment4);
|
||||
pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, FRACTIONBITS));
|
||||
frac4 = _mm_and_si128(frac4, fracMask4);
|
||||
}
|
||||
|
||||
/* NOTE: These four elements represent the position *after* the last four
|
||||
* samples, so the lowest element is the next position to resample.
|
||||
*/
|
||||
pos = _mm_cvtsi128_si32(pos4);
|
||||
frac = _mm_cvtsi128_si32(frac4);
|
||||
|
||||
for(;i < numsamples;++i)
|
||||
{
|
||||
dst[i] = lerp(src[pos], src[pos+1], frac * (1.0f/FRACTIONONE));
|
||||
|
||||
frac += increment;
|
||||
pos += frac>>FRACTIONBITS;
|
||||
frac &= FRACTIONMASK;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
+762
@@ -0,0 +1,762 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "alMain.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alSource.h"
|
||||
#include "alBuffer.h"
|
||||
#include "alListener.h"
|
||||
#include "alAuxEffectSlot.h"
|
||||
#include "sample_cvt.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include "cpu_caps.h"
|
||||
#include "mixer/defs.h"
|
||||
|
||||
|
||||
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
|
||||
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
|
||||
|
||||
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALsizei *restrict pos_arr, ALsizei size);
|
||||
|
||||
|
||||
/* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
|
||||
static_assert(MAX_RESAMPLE_PADDING >= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
|
||||
|
||||
|
||||
enum Resampler ResamplerDefault = LinearResampler;
|
||||
|
||||
MixerFunc MixSamples = Mix_C;
|
||||
RowMixerFunc MixRowSamples = MixRow_C;
|
||||
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
|
||||
static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
|
||||
|
||||
static MixerFunc SelectMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Mix_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_SSE;
|
||||
#endif
|
||||
return Mix_C;
|
||||
}
|
||||
|
||||
static RowMixerFunc SelectRowMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixRow_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixRow_SSE;
|
||||
#endif
|
||||
return MixRow_C;
|
||||
}
|
||||
|
||||
static inline HrtfMixerFunc SelectHrtfMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtf_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtf_SSE;
|
||||
#endif
|
||||
return MixHrtf_C;
|
||||
}
|
||||
|
||||
static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return MixHrtfBlend_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return MixHrtfBlend_SSE;
|
||||
#endif
|
||||
return MixHrtfBlend_C;
|
||||
}
|
||||
|
||||
ResamplerFunc SelectResampler(enum Resampler resampler)
|
||||
{
|
||||
switch(resampler)
|
||||
{
|
||||
case PointResampler:
|
||||
return Resample_point_C;
|
||||
case LinearResampler:
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Resample_lerp_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE4_1
|
||||
if((CPUCapFlags&CPU_CAP_SSE4_1))
|
||||
return Resample_lerp_SSE41;
|
||||
#endif
|
||||
#ifdef HAVE_SSE2
|
||||
if((CPUCapFlags&CPU_CAP_SSE2))
|
||||
return Resample_lerp_SSE2;
|
||||
#endif
|
||||
return Resample_lerp_C;
|
||||
case FIR4Resampler:
|
||||
return Resample_cubic_C;
|
||||
case BSinc12Resampler:
|
||||
case BSinc24Resampler:
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Resample_bsinc_Neon;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Resample_bsinc_SSE;
|
||||
#endif
|
||||
return Resample_bsinc_C;
|
||||
}
|
||||
|
||||
return Resample_point_C;
|
||||
}
|
||||
|
||||
|
||||
void aluInitMixer(void)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if(ConfigValueStr(NULL, NULL, "resampler", &str))
|
||||
{
|
||||
if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
|
||||
ResamplerDefault = PointResampler;
|
||||
else if(strcasecmp(str, "linear") == 0)
|
||||
ResamplerDefault = LinearResampler;
|
||||
else if(strcasecmp(str, "cubic") == 0)
|
||||
ResamplerDefault = FIR4Resampler;
|
||||
else if(strcasecmp(str, "bsinc12") == 0)
|
||||
ResamplerDefault = BSinc12Resampler;
|
||||
else if(strcasecmp(str, "bsinc24") == 0)
|
||||
ResamplerDefault = BSinc24Resampler;
|
||||
else if(strcasecmp(str, "bsinc") == 0)
|
||||
{
|
||||
WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
|
||||
ResamplerDefault = BSinc12Resampler;
|
||||
}
|
||||
else if(strcasecmp(str, "sinc4") == 0 || strcasecmp(str, "sinc8") == 0)
|
||||
{
|
||||
WARN("Resampler option \"%s\" is deprecated, using cubic\n", str);
|
||||
ResamplerDefault = FIR4Resampler;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *end;
|
||||
long n = strtol(str, &end, 0);
|
||||
if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
|
||||
ResamplerDefault = n;
|
||||
else
|
||||
WARN("Invalid resampler: %s\n", str);
|
||||
}
|
||||
}
|
||||
|
||||
MixHrtfBlendSamples = SelectHrtfBlendMixer();
|
||||
MixHrtfSamples = SelectHrtfMixer();
|
||||
MixSamples = SelectMixer();
|
||||
MixRowSamples = SelectRowMixer();
|
||||
}
|
||||
|
||||
|
||||
static void SendAsyncEvent(ALCcontext *context, ALuint enumtype, ALenum type,
|
||||
ALuint objid, ALuint param, const char *msg)
|
||||
{
|
||||
AsyncEvent evt = ASYNC_EVENT(enumtype);
|
||||
evt.u.user.type = type;
|
||||
evt.u.user.id = objid;
|
||||
evt.u.user.param = param;
|
||||
strcpy(evt.u.user.msg, msg);
|
||||
if(ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1) == 1)
|
||||
alsem_post(&context->EventSem);
|
||||
}
|
||||
|
||||
|
||||
static inline ALfloat Sample_ALubyte(ALubyte val)
|
||||
{ return (val-128) * (1.0f/128.0f); }
|
||||
|
||||
static inline ALfloat Sample_ALshort(ALshort val)
|
||||
{ return val * (1.0f/32768.0f); }
|
||||
|
||||
static inline ALfloat Sample_ALfloat(ALfloat val)
|
||||
{ return val; }
|
||||
|
||||
static inline ALfloat Sample_ALdouble(ALdouble val)
|
||||
{ return (ALfloat)val; }
|
||||
|
||||
typedef ALubyte ALmulaw;
|
||||
static inline ALfloat Sample_ALmulaw(ALmulaw val)
|
||||
{ return muLawDecompressionTable[val] * (1.0f/32768.0f); }
|
||||
|
||||
typedef ALubyte ALalaw;
|
||||
static inline ALfloat Sample_ALalaw(ALalaw val)
|
||||
{ return aLawDecompressionTable[val] * (1.0f/32768.0f); }
|
||||
|
||||
#define DECL_TEMPLATE(T) \
|
||||
static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
|
||||
ALint srcstep, ALsizei samples) \
|
||||
{ \
|
||||
ALsizei i; \
|
||||
for(i = 0;i < samples;i++) \
|
||||
dst[i] += Sample_##T(src[i*srcstep]); \
|
||||
}
|
||||
|
||||
DECL_TEMPLATE(ALubyte)
|
||||
DECL_TEMPLATE(ALshort)
|
||||
DECL_TEMPLATE(ALfloat)
|
||||
DECL_TEMPLATE(ALdouble)
|
||||
DECL_TEMPLATE(ALmulaw)
|
||||
DECL_TEMPLATE(ALalaw)
|
||||
|
||||
#undef DECL_TEMPLATE
|
||||
|
||||
static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint srcstep,
|
||||
enum FmtType srctype, ALsizei samples)
|
||||
{
|
||||
#define HANDLE_FMT(ET, ST) case ET: Load_##ST(dst, src, srcstep, samples); break
|
||||
switch(srctype)
|
||||
{
|
||||
HANDLE_FMT(FmtUByte, ALubyte);
|
||||
HANDLE_FMT(FmtShort, ALshort);
|
||||
HANDLE_FMT(FmtFloat, ALfloat);
|
||||
HANDLE_FMT(FmtDouble, ALdouble);
|
||||
HANDLE_FMT(FmtMulaw, ALmulaw);
|
||||
HANDLE_FMT(FmtAlaw, ALalaw);
|
||||
}
|
||||
#undef HANDLE_FMT
|
||||
}
|
||||
|
||||
|
||||
static const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
|
||||
ALfloat *restrict dst, const ALfloat *restrict src,
|
||||
ALsizei numsamples, enum ActiveFilters type)
|
||||
{
|
||||
ALsizei i;
|
||||
switch(type)
|
||||
{
|
||||
case AF_None:
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
break;
|
||||
|
||||
case AF_LowPass:
|
||||
BiquadFilter_process(lpfilter, dst, src, numsamples);
|
||||
BiquadFilter_passthru(hpfilter, numsamples);
|
||||
return dst;
|
||||
case AF_HighPass:
|
||||
BiquadFilter_passthru(lpfilter, numsamples);
|
||||
BiquadFilter_process(hpfilter, dst, src, numsamples);
|
||||
return dst;
|
||||
|
||||
case AF_BandPass:
|
||||
for(i = 0;i < numsamples;)
|
||||
{
|
||||
ALfloat temp[256];
|
||||
ALsizei todo = mini(256, numsamples-i);
|
||||
|
||||
BiquadFilter_process(lpfilter, temp, src+i, todo);
|
||||
BiquadFilter_process(hpfilter, dst+i, temp, todo);
|
||||
i += todo;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
/* This function uses these device temp buffers. */
|
||||
#define SOURCE_DATA_BUF 0
|
||||
#define RESAMPLED_BUF 1
|
||||
#define FILTERED_BUF 2
|
||||
#define NFC_DATA_BUF 3
|
||||
ALboolean MixSource(ALvoice *voice, ALuint SourceID, ALCcontext *Context, ALsizei SamplesToDo)
|
||||
{
|
||||
ALCdevice *Device = Context->Device;
|
||||
ALbufferlistitem *BufferListItem;
|
||||
ALbufferlistitem *BufferLoopItem;
|
||||
ALsizei NumChannels, SampleSize;
|
||||
ALbitfieldSOFT enabledevt;
|
||||
ALsizei buffers_done = 0;
|
||||
ResamplerFunc Resample;
|
||||
ALsizei DataPosInt;
|
||||
ALsizei DataPosFrac;
|
||||
ALint64 DataSize64;
|
||||
ALint increment;
|
||||
ALsizei Counter;
|
||||
ALsizei OutPos;
|
||||
ALsizei IrSize;
|
||||
bool isplaying;
|
||||
bool firstpass;
|
||||
bool isstatic;
|
||||
ALsizei chan;
|
||||
ALsizei send;
|
||||
|
||||
/* Get source info */
|
||||
isplaying = true; /* Will only be called while playing. */
|
||||
isstatic = !!(voice->Flags&VOICE_IS_STATIC);
|
||||
DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
|
||||
DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
|
||||
BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
|
||||
BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
|
||||
NumChannels = voice->NumChannels;
|
||||
SampleSize = voice->SampleSize;
|
||||
increment = voice->Step;
|
||||
|
||||
IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
|
||||
|
||||
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
|
||||
Resample_copy_C : voice->Resampler);
|
||||
|
||||
Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
|
||||
firstpass = true;
|
||||
OutPos = 0;
|
||||
|
||||
do {
|
||||
ALsizei SrcBufferSize, DstBufferSize;
|
||||
|
||||
/* Figure out how many buffer samples will be needed */
|
||||
DataSize64 = SamplesToDo-OutPos;
|
||||
DataSize64 *= increment;
|
||||
DataSize64 += DataPosFrac+FRACTIONMASK;
|
||||
DataSize64 >>= FRACTIONBITS;
|
||||
DataSize64 += MAX_RESAMPLE_PADDING*2;
|
||||
SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
|
||||
|
||||
/* Figure out how many samples we can actually mix from this. */
|
||||
DataSize64 = SrcBufferSize;
|
||||
DataSize64 -= MAX_RESAMPLE_PADDING*2;
|
||||
DataSize64 <<= FRACTIONBITS;
|
||||
DataSize64 -= DataPosFrac;
|
||||
DstBufferSize = (ALsizei)mini64((DataSize64+(increment-1)) / increment,
|
||||
SamplesToDo - OutPos);
|
||||
|
||||
/* Some mixers like having a multiple of 4, so try to give that unless
|
||||
* this is the last update. */
|
||||
if(DstBufferSize < SamplesToDo-OutPos)
|
||||
DstBufferSize &= ~3;
|
||||
|
||||
/* It's impossible to have a buffer list item with no entries. */
|
||||
assert(BufferListItem->num_buffers > 0);
|
||||
|
||||
for(chan = 0;chan < NumChannels;chan++)
|
||||
{
|
||||
const ALfloat *ResampledData;
|
||||
ALfloat *SrcData = Device->TempBuffer[SOURCE_DATA_BUF];
|
||||
ALsizei FilledAmt;
|
||||
|
||||
/* Load the previous samples into the source data first, and clear the rest. */
|
||||
memcpy(SrcData, voice->PrevSamples[chan], MAX_RESAMPLE_PADDING*sizeof(ALfloat));
|
||||
memset(SrcData+MAX_RESAMPLE_PADDING, 0, (BUFFERSIZE-MAX_RESAMPLE_PADDING)*
|
||||
sizeof(ALfloat));
|
||||
FilledAmt = MAX_RESAMPLE_PADDING;
|
||||
|
||||
if(isstatic)
|
||||
{
|
||||
/* TODO: For static sources, loop points are taken from the
|
||||
* first buffer (should be adjusted by any buffer offset, to
|
||||
* possibly be added later).
|
||||
*/
|
||||
const ALbuffer *Buffer0 = BufferListItem->buffers[0];
|
||||
const ALsizei LoopStart = Buffer0->LoopStart;
|
||||
const ALsizei LoopEnd = Buffer0->LoopEnd;
|
||||
const ALsizei LoopSize = LoopEnd - LoopStart;
|
||||
|
||||
/* If current pos is beyond the loop range, do not loop */
|
||||
if(!BufferLoopItem || DataPosInt >= LoopEnd)
|
||||
{
|
||||
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
BufferLoopItem = NULL;
|
||||
|
||||
for(i = 0;i < BufferListItem->num_buffers;i++)
|
||||
{
|
||||
const ALbuffer *buffer = BufferListItem->buffers[i];
|
||||
const ALubyte *Data = buffer->data;
|
||||
ALsizei DataSize;
|
||||
|
||||
if(DataPosInt >= buffer->SampleLen)
|
||||
continue;
|
||||
|
||||
/* Load what's left to play from the buffer */
|
||||
DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[FilledAmt],
|
||||
&Data[(DataPosInt*NumChannels + chan)*SampleSize],
|
||||
NumChannels, buffer->FmtType, DataSize
|
||||
);
|
||||
}
|
||||
FilledAmt += CompLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopEnd - DataPosInt);
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < BufferListItem->num_buffers;i++)
|
||||
{
|
||||
const ALbuffer *buffer = BufferListItem->buffers[i];
|
||||
const ALubyte *Data = buffer->data;
|
||||
ALsizei DataSize;
|
||||
|
||||
if(DataPosInt >= buffer->SampleLen)
|
||||
continue;
|
||||
|
||||
/* Load what's left of this loop iteration */
|
||||
DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[FilledAmt],
|
||||
&Data[(DataPosInt*NumChannels + chan)*SampleSize],
|
||||
NumChannels, buffer->FmtType, DataSize
|
||||
);
|
||||
}
|
||||
FilledAmt += CompLen;
|
||||
|
||||
while(SrcBufferSize > FilledAmt)
|
||||
{
|
||||
const ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopSize);
|
||||
|
||||
CompLen = 0;
|
||||
for(i = 0;i < BufferListItem->num_buffers;i++)
|
||||
{
|
||||
const ALbuffer *buffer = BufferListItem->buffers[i];
|
||||
const ALubyte *Data = buffer->data;
|
||||
ALsizei DataSize;
|
||||
|
||||
if(LoopStart >= buffer->SampleLen)
|
||||
continue;
|
||||
|
||||
DataSize = mini(SizeToDo, buffer->SampleLen - LoopStart);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[FilledAmt],
|
||||
&Data[(LoopStart*NumChannels + chan)*SampleSize],
|
||||
NumChannels, buffer->FmtType, DataSize
|
||||
);
|
||||
}
|
||||
FilledAmt += CompLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Crawl the buffer queue to fill in the temp buffer */
|
||||
ALbufferlistitem *tmpiter = BufferListItem;
|
||||
ALsizei pos = DataPosInt;
|
||||
|
||||
while(tmpiter && SrcBufferSize > FilledAmt)
|
||||
{
|
||||
ALsizei SizeToDo = SrcBufferSize - FilledAmt;
|
||||
ALsizei CompLen = 0;
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < tmpiter->num_buffers;i++)
|
||||
{
|
||||
const ALbuffer *ALBuffer = tmpiter->buffers[i];
|
||||
ALsizei DataSize = ALBuffer ? ALBuffer->SampleLen : 0;
|
||||
|
||||
if(DataSize > pos)
|
||||
{
|
||||
const ALubyte *Data = ALBuffer->data;
|
||||
Data += (pos*NumChannels + chan)*SampleSize;
|
||||
|
||||
DataSize = mini(SizeToDo, DataSize - pos);
|
||||
CompLen = maxi(CompLen, DataSize);
|
||||
|
||||
LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
|
||||
ALBuffer->FmtType, DataSize);
|
||||
}
|
||||
}
|
||||
if(UNLIKELY(!CompLen))
|
||||
pos -= tmpiter->max_samples;
|
||||
else
|
||||
{
|
||||
FilledAmt += CompLen;
|
||||
if(SrcBufferSize <= FilledAmt)
|
||||
break;
|
||||
pos = 0;
|
||||
}
|
||||
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
|
||||
if(!tmpiter) tmpiter = BufferLoopItem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store the last source samples used for next time. */
|
||||
memcpy(voice->PrevSamples[chan],
|
||||
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
|
||||
MAX_RESAMPLE_PADDING*sizeof(ALfloat)
|
||||
);
|
||||
|
||||
/* Now resample, then filter and mix to the appropriate outputs. */
|
||||
ResampledData = Resample(&voice->ResampleState,
|
||||
&SrcData[MAX_RESAMPLE_PADDING], DataPosFrac, increment,
|
||||
Device->TempBuffer[RESAMPLED_BUF], DstBufferSize
|
||||
);
|
||||
{
|
||||
DirectParams *parms = &voice->Direct.Params[chan];
|
||||
const ALfloat *samples;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
|
||||
ResampledData, DstBufferSize, voice->Direct.FilterType
|
||||
);
|
||||
if(!(voice->Flags&VOICE_HAS_HRTF))
|
||||
{
|
||||
if(!Counter)
|
||||
memcpy(parms->Gains.Current, parms->Gains.Target,
|
||||
sizeof(parms->Gains.Current));
|
||||
if(!(voice->Flags&VOICE_HAS_NFC))
|
||||
MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
|
||||
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
|
||||
DstBufferSize
|
||||
);
|
||||
else
|
||||
{
|
||||
ALfloat *nfcsamples = Device->TempBuffer[NFC_DATA_BUF];
|
||||
ALsizei chanoffset = 0;
|
||||
|
||||
MixSamples(samples,
|
||||
voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
|
||||
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
|
||||
DstBufferSize
|
||||
);
|
||||
chanoffset += voice->Direct.ChannelsPerOrder[0];
|
||||
#define APPLY_NFC_MIX(order) \
|
||||
if(voice->Direct.ChannelsPerOrder[order] > 0) \
|
||||
{ \
|
||||
NfcFilterProcess##order(&parms->NFCtrlFilter, nfcsamples, samples, \
|
||||
DstBufferSize); \
|
||||
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
|
||||
voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
|
||||
parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
|
||||
); \
|
||||
chanoffset += voice->Direct.ChannelsPerOrder[order]; \
|
||||
}
|
||||
APPLY_NFC_MIX(1)
|
||||
APPLY_NFC_MIX(2)
|
||||
APPLY_NFC_MIX(3)
|
||||
#undef APPLY_NFC_MIX
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MixHrtfParams hrtfparams;
|
||||
ALsizei fademix = 0;
|
||||
int lidx, ridx;
|
||||
|
||||
lidx = GetChannelIdxByName(&Device->RealOut, FrontLeft);
|
||||
ridx = GetChannelIdxByName(&Device->RealOut, FrontRight);
|
||||
assert(lidx != -1 && ridx != -1);
|
||||
|
||||
if(!Counter)
|
||||
{
|
||||
/* No fading, just overwrite the old HRTF params. */
|
||||
parms->Hrtf.Old = parms->Hrtf.Target;
|
||||
}
|
||||
else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
|
||||
{
|
||||
/* The old HRTF params are silent, so overwrite the old
|
||||
* coefficients with the new, and reset the old gain to
|
||||
* 0. The future mix will then fade from silence.
|
||||
*/
|
||||
parms->Hrtf.Old = parms->Hrtf.Target;
|
||||
parms->Hrtf.Old.Gain = 0.0f;
|
||||
}
|
||||
else if(firstpass)
|
||||
{
|
||||
ALfloat gain;
|
||||
|
||||
/* Fade between the coefficients over 128 samples. */
|
||||
fademix = mini(DstBufferSize, 128);
|
||||
|
||||
/* The new coefficients need to fade in completely
|
||||
* since they're replacing the old ones. To keep the
|
||||
* gain fading consistent, interpolate between the old
|
||||
* and new target gains given how much of the fade time
|
||||
* this mix handles.
|
||||
*/
|
||||
gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
|
||||
minf(1.0f, (ALfloat)fademix/Counter));
|
||||
hrtfparams.Coeffs = parms->Hrtf.Target.Coeffs;
|
||||
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
|
||||
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
|
||||
hrtfparams.Gain = 0.0f;
|
||||
hrtfparams.GainStep = gain / (ALfloat)fademix;
|
||||
|
||||
MixHrtfBlendSamples(
|
||||
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
|
||||
samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
|
||||
&hrtfparams, &parms->Hrtf.State, fademix
|
||||
);
|
||||
/* Update the old parameters with the result. */
|
||||
parms->Hrtf.Old = parms->Hrtf.Target;
|
||||
if(fademix < Counter)
|
||||
parms->Hrtf.Old.Gain = hrtfparams.Gain;
|
||||
}
|
||||
|
||||
if(fademix < DstBufferSize)
|
||||
{
|
||||
ALsizei todo = DstBufferSize - fademix;
|
||||
ALfloat gain = parms->Hrtf.Target.Gain;
|
||||
|
||||
/* Interpolate the target gain if the gain fading lasts
|
||||
* longer than this mix.
|
||||
*/
|
||||
if(Counter > DstBufferSize)
|
||||
gain = lerp(parms->Hrtf.Old.Gain, gain,
|
||||
(ALfloat)todo/(Counter-fademix));
|
||||
|
||||
hrtfparams.Coeffs = parms->Hrtf.Target.Coeffs;
|
||||
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
|
||||
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
|
||||
hrtfparams.Gain = parms->Hrtf.Old.Gain;
|
||||
hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
|
||||
MixHrtfSamples(
|
||||
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
|
||||
samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
|
||||
&hrtfparams, &parms->Hrtf.State, todo
|
||||
);
|
||||
/* Store the interpolated gain or the final target gain
|
||||
* depending if the fade is done.
|
||||
*/
|
||||
if(DstBufferSize < Counter)
|
||||
parms->Hrtf.Old.Gain = gain;
|
||||
else
|
||||
parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(send = 0;send < Device->NumAuxSends;send++)
|
||||
{
|
||||
SendParams *parms = &voice->Send[send].Params[chan];
|
||||
const ALfloat *samples;
|
||||
|
||||
if(!voice->Send[send].Buffer)
|
||||
continue;
|
||||
|
||||
samples = DoFilters(
|
||||
&parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
|
||||
ResampledData, DstBufferSize, voice->Send[send].FilterType
|
||||
);
|
||||
|
||||
if(!Counter)
|
||||
memcpy(parms->Gains.Current, parms->Gains.Target,
|
||||
sizeof(parms->Gains.Current));
|
||||
MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
|
||||
parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
|
||||
);
|
||||
}
|
||||
}
|
||||
/* Update positions */
|
||||
DataPosFrac += increment*DstBufferSize;
|
||||
DataPosInt += DataPosFrac>>FRACTIONBITS;
|
||||
DataPosFrac &= FRACTIONMASK;
|
||||
|
||||
OutPos += DstBufferSize;
|
||||
voice->Offset += DstBufferSize;
|
||||
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
|
||||
firstpass = false;
|
||||
|
||||
if(isstatic)
|
||||
{
|
||||
if(BufferLoopItem)
|
||||
{
|
||||
/* Handle looping static source */
|
||||
const ALbuffer *Buffer = BufferListItem->buffers[0];
|
||||
ALsizei LoopStart = Buffer->LoopStart;
|
||||
ALsizei LoopEnd = Buffer->LoopEnd;
|
||||
if(DataPosInt >= LoopEnd)
|
||||
{
|
||||
assert(LoopEnd > LoopStart);
|
||||
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Handle non-looping static source */
|
||||
if(DataPosInt >= BufferListItem->max_samples)
|
||||
{
|
||||
isplaying = false;
|
||||
BufferListItem = NULL;
|
||||
DataPosInt = 0;
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else while(1)
|
||||
{
|
||||
/* Handle streaming source */
|
||||
if(BufferListItem->max_samples > DataPosInt)
|
||||
break;
|
||||
|
||||
DataPosInt -= BufferListItem->max_samples;
|
||||
|
||||
buffers_done += BufferListItem->num_buffers;
|
||||
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_relaxed);
|
||||
if(!BufferListItem && !(BufferListItem=BufferLoopItem))
|
||||
{
|
||||
isplaying = false;
|
||||
DataPosInt = 0;
|
||||
DataPosFrac = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(isplaying && OutPos < SamplesToDo);
|
||||
|
||||
voice->Flags |= VOICE_IS_FADING;
|
||||
|
||||
/* Update source info */
|
||||
ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed);
|
||||
ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
|
||||
ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release);
|
||||
|
||||
/* Send any events now, after the position/buffer info was updated. */
|
||||
enabledevt = ATOMIC_LOAD(&Context->EnabledEvts, almemory_order_acquire);
|
||||
if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted))
|
||||
SendAsyncEvent(Context, EventType_BufferCompleted,
|
||||
AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, SourceID, buffers_done, "Buffer completed"
|
||||
);
|
||||
|
||||
return isplaying;
|
||||
}
|
||||
+1146
-225
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
#ifndef POLYMORPHISM_H
|
||||
#define POLYMORPHISM_H
|
||||
|
||||
/* Macros to declare inheriting types, and to (down-)cast and up-cast. */
|
||||
#define DERIVE_FROM_TYPE(t) t t##_parent
|
||||
#define STATIC_CAST(to, obj) (&(obj)->to##_parent)
|
||||
#ifdef __GNUC__
|
||||
#define STATIC_UPCAST(to, from, obj) __extension__({ \
|
||||
static_assert(__builtin_types_compatible_p(from, __typeof(*(obj))), \
|
||||
"Invalid upcast object from type"); \
|
||||
(to*)((char*)(obj) - offsetof(to, from##_parent)); \
|
||||
})
|
||||
#else
|
||||
#define STATIC_UPCAST(to, from, obj) ((to*)((char*)(obj) - offsetof(to, from##_parent)))
|
||||
#endif
|
||||
|
||||
/* Defines method forwards, which call the given parent's (T2's) implementation. */
|
||||
#define DECLARE_FORWARD(T1, T2, rettype, func) \
|
||||
rettype T1##_##func(T1 *obj) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj)); }
|
||||
|
||||
#define DECLARE_FORWARD1(T1, T2, rettype, func, argtype1) \
|
||||
rettype T1##_##func(T1 *obj, argtype1 a) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj), a); }
|
||||
|
||||
#define DECLARE_FORWARD2(T1, T2, rettype, func, argtype1, argtype2) \
|
||||
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj), a, b); }
|
||||
|
||||
#define DECLARE_FORWARD3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
|
||||
rettype T1##_##func(T1 *obj, argtype1 a, argtype2 b, argtype3 c) \
|
||||
{ return T2##_##func(STATIC_CAST(T2, obj), a, b, c); }
|
||||
|
||||
/* Defines method thunks, functions that call to the child's method. */
|
||||
#define DECLARE_THUNK(T1, T2, rettype, func) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj)); }
|
||||
|
||||
#define DECLARE_THUNK1(T1, T2, rettype, func, argtype1) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a); }
|
||||
|
||||
#define DECLARE_THUNK2(T1, T2, rettype, func, argtype1, argtype2) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b); }
|
||||
|
||||
#define DECLARE_THUNK3(T1, T2, rettype, func, argtype1, argtype2, argtype3) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c); }
|
||||
|
||||
#define DECLARE_THUNK4(T1, T2, rettype, func, argtype1, argtype2, argtype3, argtype4) \
|
||||
static rettype T1##_##T2##_##func(T2 *obj, argtype1 a, argtype2 b, argtype3 c, argtype4 d) \
|
||||
{ return T1##_##func(STATIC_UPCAST(T1, T2, obj), a, b, c, d); }
|
||||
|
||||
/* Defines the default functions used to (de)allocate a polymorphic object. */
|
||||
#define DECLARE_DEFAULT_ALLOCATORS(T) \
|
||||
static void* T##_New(size_t size) { return al_malloc(16, size); } \
|
||||
static void T##_Delete(void *ptr) { al_free(ptr); }
|
||||
|
||||
|
||||
/* Helper to extract an argument list for virtual method calls. */
|
||||
#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
|
||||
|
||||
/* Call a "virtual" method on an object, with arguments. */
|
||||
#define V(obj, func) ((obj)->vtbl->func((obj), EXTRACT_VCALL_ARGS
|
||||
/* Call a "virtual" method on an object, with no arguments. */
|
||||
#define V0(obj, func) ((obj)->vtbl->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
|
||||
/* Helper to extract an argument list for NEW_OBJ calls. */
|
||||
#define EXTRACT_NEW_ARGS(...) __VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* Allocate and construct an object, with arguments. */
|
||||
#define NEW_OBJ(_res, T) do { \
|
||||
_res = T##_New(sizeof(T)); \
|
||||
if(_res) \
|
||||
{ \
|
||||
memset(_res, 0, sizeof(T)); \
|
||||
T##_Construct(_res, EXTRACT_NEW_ARGS
|
||||
/* Allocate and construct an object, with no arguments. */
|
||||
#define NEW_OBJ0(_res, T) do { \
|
||||
_res = T##_New(sizeof(T)); \
|
||||
if(_res) \
|
||||
{ \
|
||||
memset(_res, 0, sizeof(T)); \
|
||||
T##_Construct(_res EXTRACT_NEW_ARGS
|
||||
|
||||
/* Destructs and deallocate an object. */
|
||||
#define DELETE_OBJ(obj) do { \
|
||||
if((obj) != NULL) \
|
||||
{ \
|
||||
V0((obj),Destruct)(); \
|
||||
V0((obj),Delete)(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* Helper to get a type's vtable thunk for a child type. */
|
||||
#define GET_VTABLE2(T1, T2) (&(T1##_##T2##_vtable))
|
||||
/* Helper to set an object's vtable thunk for a child type. Used when constructing an object. */
|
||||
#define SET_VTABLE2(T1, T2, obj) (STATIC_CAST(T2, obj)->vtbl = GET_VTABLE2(T1, T2))
|
||||
|
||||
#endif /* POLYMORPHISM_H */
|
||||
@@ -0,0 +1,295 @@
|
||||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* Copyright (C) 1999-2007 by authors.
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
#include "align.h"
|
||||
#include "atomic.h"
|
||||
#include "threads.h"
|
||||
#include "almalloc.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
/* NOTE: This lockless ringbuffer implementation is copied from JACK, extended
|
||||
* to include an element size. Consequently, parameters and return values for a
|
||||
* size or count is in 'elements', not bytes. Additionally, it only supports
|
||||
* single-consumer/single-provider operation. */
|
||||
struct ll_ringbuffer {
|
||||
ATOMIC(size_t) write_ptr;
|
||||
ATOMIC(size_t) read_ptr;
|
||||
size_t size;
|
||||
size_t size_mask;
|
||||
size_t elem_size;
|
||||
|
||||
alignas(16) char buf[];
|
||||
};
|
||||
|
||||
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes)
|
||||
{
|
||||
ll_ringbuffer_t *rb;
|
||||
size_t power_of_two = 0;
|
||||
|
||||
if(sz > 0)
|
||||
{
|
||||
power_of_two = sz;
|
||||
power_of_two |= power_of_two>>1;
|
||||
power_of_two |= power_of_two>>2;
|
||||
power_of_two |= power_of_two>>4;
|
||||
power_of_two |= power_of_two>>8;
|
||||
power_of_two |= power_of_two>>16;
|
||||
#if SIZE_MAX > UINT_MAX
|
||||
power_of_two |= power_of_two>>32;
|
||||
#endif
|
||||
}
|
||||
power_of_two++;
|
||||
if(power_of_two < sz) return NULL;
|
||||
|
||||
rb = al_malloc(16, sizeof(*rb) + power_of_two*elem_sz);
|
||||
if(!rb) return NULL;
|
||||
|
||||
ATOMIC_INIT(&rb->write_ptr, 0);
|
||||
ATOMIC_INIT(&rb->read_ptr, 0);
|
||||
rb->size = limit_writes ? sz : power_of_two;
|
||||
rb->size_mask = power_of_two - 1;
|
||||
rb->elem_size = elem_sz;
|
||||
return rb;
|
||||
}
|
||||
|
||||
void ll_ringbuffer_free(ll_ringbuffer_t *rb)
|
||||
{
|
||||
al_free(rb);
|
||||
}
|
||||
|
||||
void ll_ringbuffer_reset(ll_ringbuffer_t *rb)
|
||||
{
|
||||
ATOMIC_STORE(&rb->write_ptr, 0, almemory_order_release);
|
||||
ATOMIC_STORE(&rb->read_ptr, 0, almemory_order_release);
|
||||
memset(rb->buf, 0, (rb->size_mask+1)*rb->elem_size);
|
||||
}
|
||||
|
||||
|
||||
size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb)
|
||||
{
|
||||
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
return (w-r) & rb->size_mask;
|
||||
}
|
||||
|
||||
size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb)
|
||||
{
|
||||
size_t w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
size_t r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
w = (r-w-1) & rb->size_mask;
|
||||
return (w > rb->size) ? rb->size : w;
|
||||
}
|
||||
|
||||
|
||||
size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
{
|
||||
size_t read_ptr;
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t to_read;
|
||||
size_t n1, n2;
|
||||
|
||||
free_cnt = ll_ringbuffer_read_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_read = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = read_ptr + to_read;
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
n1 = rb->size_mask+1 - read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
|
||||
read_ptr += n1;
|
||||
if(n2)
|
||||
{
|
||||
memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
|
||||
n2*rb->elem_size);
|
||||
read_ptr += n2;
|
||||
}
|
||||
ATOMIC_STORE(&rb->read_ptr, read_ptr, almemory_order_release);
|
||||
return to_read;
|
||||
}
|
||||
|
||||
size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt)
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t to_read;
|
||||
size_t n1, n2;
|
||||
size_t read_ptr;
|
||||
|
||||
free_cnt = ll_ringbuffer_read_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_read = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
read_ptr = ATOMIC_LOAD(&rb->read_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = read_ptr + to_read;
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
n1 = rb->size_mask+1 - read_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_read;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(dest, &rb->buf[read_ptr*rb->elem_size], n1*rb->elem_size);
|
||||
if(n2)
|
||||
{
|
||||
read_ptr += n1;
|
||||
memcpy(dest + n1*rb->elem_size, &rb->buf[(read_ptr&rb->size_mask)*rb->elem_size],
|
||||
n2*rb->elem_size);
|
||||
}
|
||||
return to_read;
|
||||
}
|
||||
|
||||
size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt)
|
||||
{
|
||||
size_t write_ptr;
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t to_write;
|
||||
size_t n1, n2;
|
||||
|
||||
free_cnt = ll_ringbuffer_write_space(rb);
|
||||
if(free_cnt == 0) return 0;
|
||||
|
||||
to_write = (cnt > free_cnt) ? free_cnt : cnt;
|
||||
write_ptr = ATOMIC_LOAD(&rb->write_ptr, almemory_order_relaxed) & rb->size_mask;
|
||||
|
||||
cnt2 = write_ptr + to_write;
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
n1 = rb->size_mask+1 - write_ptr;
|
||||
n2 = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = to_write;
|
||||
n2 = 0;
|
||||
}
|
||||
|
||||
memcpy(&rb->buf[write_ptr*rb->elem_size], src, n1*rb->elem_size);
|
||||
write_ptr += n1;
|
||||
if(n2)
|
||||
{
|
||||
memcpy(&rb->buf[(write_ptr&rb->size_mask)*rb->elem_size], src + n1*rb->elem_size,
|
||||
n2*rb->elem_size);
|
||||
write_ptr += n2;
|
||||
}
|
||||
ATOMIC_STORE(&rb->write_ptr, write_ptr, almemory_order_release);
|
||||
return to_write;
|
||||
}
|
||||
|
||||
|
||||
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
ATOMIC_ADD(&rb->read_ptr, cnt, almemory_order_acq_rel);
|
||||
}
|
||||
|
||||
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt)
|
||||
{
|
||||
ATOMIC_ADD(&rb->write_ptr, cnt, almemory_order_acq_rel);
|
||||
}
|
||||
|
||||
|
||||
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t w, r;
|
||||
|
||||
w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
w &= rb->size_mask;
|
||||
r &= rb->size_mask;
|
||||
free_cnt = (w-r) & rb->size_mask;
|
||||
|
||||
cnt2 = r + free_cnt;
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
/* Two part vector: the rest of the buffer after the current write ptr,
|
||||
* plus some from the start of the buffer. */
|
||||
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
|
||||
vec[0].len = rb->size_mask+1 - r;
|
||||
vec[1].buf = (char*)rb->buf;
|
||||
vec[1].len = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Single part vector: just the rest of the buffer */
|
||||
vec[0].buf = (char*)&rb->buf[r*rb->elem_size];
|
||||
vec[0].len = free_cnt;
|
||||
vec[1].buf = NULL;
|
||||
vec[1].len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2])
|
||||
{
|
||||
size_t free_cnt;
|
||||
size_t cnt2;
|
||||
size_t w, r;
|
||||
|
||||
w = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->write_ptr, almemory_order_acquire);
|
||||
r = ATOMIC_LOAD(&CONST_CAST(ll_ringbuffer_t*,rb)->read_ptr, almemory_order_acquire);
|
||||
w &= rb->size_mask;
|
||||
r &= rb->size_mask;
|
||||
free_cnt = (r-w-1) & rb->size_mask;
|
||||
if(free_cnt > rb->size) free_cnt = rb->size;
|
||||
|
||||
cnt2 = w + free_cnt;
|
||||
if(cnt2 > rb->size_mask+1)
|
||||
{
|
||||
/* Two part vector: the rest of the buffer after the current write ptr,
|
||||
* plus some from the start of the buffer. */
|
||||
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
|
||||
vec[0].len = rb->size_mask+1 - w;
|
||||
vec[1].buf = (char*)rb->buf;
|
||||
vec[1].len = cnt2 & rb->size_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec[0].buf = (char*)&rb->buf[w*rb->elem_size];
|
||||
vec[0].len = free_cnt;
|
||||
vec[1].buf = NULL;
|
||||
vec[1].len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef RINGBUFFER_H
|
||||
#define RINGBUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ll_ringbuffer ll_ringbuffer_t;
|
||||
typedef struct ll_ringbuffer_data {
|
||||
char *buf;
|
||||
size_t len;
|
||||
} ll_ringbuffer_data_t;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ringbuffer to hold at least `sz' elements of `elem_sz' bytes.
|
||||
* The number of elements is rounded up to the next power of two (even if it is
|
||||
* already a power of two, to ensure the requested amount can be written).
|
||||
*/
|
||||
ll_ringbuffer_t *ll_ringbuffer_create(size_t sz, size_t elem_sz, int limit_writes);
|
||||
/** Free all data associated with the ringbuffer `rb'. */
|
||||
void ll_ringbuffer_free(ll_ringbuffer_t *rb);
|
||||
/** Reset the read and write pointers to zero. This is not thread safe. */
|
||||
void ll_ringbuffer_reset(ll_ringbuffer_t *rb);
|
||||
|
||||
/**
|
||||
* The non-copying data reader. `vec' is an array of two places. Set the values
|
||||
* at `vec' to hold the current readable data at `rb'. If the readable data is
|
||||
* in one segment the second segment has zero length.
|
||||
*/
|
||||
void ll_ringbuffer_get_read_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2]);
|
||||
/**
|
||||
* The non-copying data writer. `vec' is an array of two places. Set the values
|
||||
* at `vec' to hold the current writeable data at `rb'. If the writeable data
|
||||
* is in one segment the second segment has zero length.
|
||||
*/
|
||||
void ll_ringbuffer_get_write_vector(const ll_ringbuffer_t *rb, ll_ringbuffer_data_t vec[2]);
|
||||
|
||||
/**
|
||||
* Return the number of elements available for reading. This is the number of
|
||||
* elements in front of the read pointer and behind the write pointer.
|
||||
*/
|
||||
size_t ll_ringbuffer_read_space(const ll_ringbuffer_t *rb);
|
||||
/**
|
||||
* The copying data reader. Copy at most `cnt' elements from `rb' to `dest'.
|
||||
* Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t ll_ringbuffer_read(ll_ringbuffer_t *rb, char *dest, size_t cnt);
|
||||
/**
|
||||
* The copying data reader w/o read pointer advance. Copy at most `cnt'
|
||||
* elements from `rb' to `dest'. Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t ll_ringbuffer_peek(ll_ringbuffer_t *rb, char *dest, size_t cnt);
|
||||
/** Advance the read pointer `cnt' places. */
|
||||
void ll_ringbuffer_read_advance(ll_ringbuffer_t *rb, size_t cnt);
|
||||
|
||||
/**
|
||||
* Return the number of elements available for writing. This is the number of
|
||||
* elements in front of the write pointer and behind the read pointer.
|
||||
*/
|
||||
size_t ll_ringbuffer_write_space(const ll_ringbuffer_t *rb);
|
||||
/**
|
||||
* The copying data writer. Copy at most `cnt' elements to `rb' from `src'.
|
||||
* Returns the actual number of elements copied.
|
||||
*/
|
||||
size_t ll_ringbuffer_write(ll_ringbuffer_t *rb, const char *src, size_t cnt);
|
||||
/** Advance the write pointer `cnt' places. */
|
||||
void ll_ringbuffer_write_advance(ll_ringbuffer_t *rb, size_t cnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* RINGBUFFER_H */
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "alu.h"
|
||||
#include "uhjfilter.h"
|
||||
|
||||
/* This is the maximum number of samples processed for each inner loop
|
||||
* iteration. */
|
||||
#define MAX_UPDATE_SAMPLES 128
|
||||
|
||||
|
||||
static const ALfloat Filter1CoeffSqr[4] = {
|
||||
0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
|
||||
};
|
||||
static const ALfloat Filter2CoeffSqr[4] = {
|
||||
0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156685f
|
||||
};
|
||||
|
||||
static void allpass_process(AllPassState *state, ALfloat *restrict dst, const ALfloat *restrict src, const ALfloat aa, ALsizei todo)
|
||||
{
|
||||
ALfloat z1 = state->z[0];
|
||||
ALfloat z2 = state->z[1];
|
||||
ALsizei i;
|
||||
|
||||
for(i = 0;i < todo;i++)
|
||||
{
|
||||
ALfloat input = src[i];
|
||||
ALfloat output = input*aa + z1;
|
||||
z1 = z2; z2 = output*aa - input;
|
||||
dst[i] = output;
|
||||
}
|
||||
|
||||
state->z[0] = z1;
|
||||
state->z[1] = z2;
|
||||
}
|
||||
|
||||
|
||||
/* NOTE: There seems to be a bit of an inconsistency in how this encoding is
|
||||
* supposed to work. Some references, such as
|
||||
*
|
||||
* http://members.tripod.com/martin_leese/Ambisonic/UHJ_file_format.html
|
||||
*
|
||||
* specify a pre-scaling of sqrt(2) on the W channel input, while other
|
||||
* references, such as
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Ambisonic_UHJ_format#Encoding.5B1.5D
|
||||
* and
|
||||
* https://wiki.xiph.org/Ambisonics#UHJ_format
|
||||
*
|
||||
* do not. The sqrt(2) scaling is in line with B-Format decoder coefficients
|
||||
* which include such a scaling for the W channel input, however the original
|
||||
* source for this equation is a 1985 paper by Michael Gerzon, which does not
|
||||
* apparently include the scaling. Applying the extra scaling creates a louder
|
||||
* result with a narrower stereo image compared to not scaling, and I don't
|
||||
* know which is the intended result.
|
||||
*/
|
||||
|
||||
void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo)
|
||||
{
|
||||
ALfloat D[MAX_UPDATE_SAMPLES], S[MAX_UPDATE_SAMPLES];
|
||||
ALfloat temp[2][MAX_UPDATE_SAMPLES];
|
||||
ALsizei base, i;
|
||||
|
||||
ASSUME(SamplesToDo > 0);
|
||||
|
||||
for(base = 0;base < SamplesToDo;)
|
||||
{
|
||||
ALsizei todo = mini(SamplesToDo - base, MAX_UPDATE_SAMPLES);
|
||||
ASSUME(todo > 0);
|
||||
|
||||
/* D = 0.6554516*Y */
|
||||
for(i = 0;i < todo;i++)
|
||||
temp[0][i] = 0.6554516f*InSamples[2][base+i];
|
||||
allpass_process(&enc->Filter1_Y[0], temp[1], temp[0], Filter1CoeffSqr[0], todo);
|
||||
allpass_process(&enc->Filter1_Y[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
|
||||
allpass_process(&enc->Filter1_Y[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
|
||||
allpass_process(&enc->Filter1_Y[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
|
||||
/* NOTE: Filter1 requires a 1 sample delay for the final output, so
|
||||
* take the last processed sample from the previous run as the first
|
||||
* output sample.
|
||||
*/
|
||||
D[0] = enc->LastY;
|
||||
for(i = 1;i < todo;i++)
|
||||
D[i] = temp[0][i-1];
|
||||
enc->LastY = temp[0][i-1];
|
||||
|
||||
/* D += j(-0.3420201*W + 0.5098604*X) */
|
||||
for(i = 0;i < todo;i++)
|
||||
temp[0][i] = -0.3420201f*InSamples[0][base+i] +
|
||||
0.5098604f*InSamples[1][base+i];
|
||||
allpass_process(&enc->Filter2_WX[0], temp[1], temp[0], Filter2CoeffSqr[0], todo);
|
||||
allpass_process(&enc->Filter2_WX[1], temp[0], temp[1], Filter2CoeffSqr[1], todo);
|
||||
allpass_process(&enc->Filter2_WX[2], temp[1], temp[0], Filter2CoeffSqr[2], todo);
|
||||
allpass_process(&enc->Filter2_WX[3], temp[0], temp[1], Filter2CoeffSqr[3], todo);
|
||||
for(i = 0;i < todo;i++)
|
||||
D[i] += temp[0][i];
|
||||
|
||||
/* S = 0.9396926*W + 0.1855740*X */
|
||||
for(i = 0;i < todo;i++)
|
||||
temp[0][i] = 0.9396926f*InSamples[0][base+i] +
|
||||
0.1855740f*InSamples[1][base+i];
|
||||
allpass_process(&enc->Filter1_WX[0], temp[1], temp[0], Filter1CoeffSqr[0], todo);
|
||||
allpass_process(&enc->Filter1_WX[1], temp[0], temp[1], Filter1CoeffSqr[1], todo);
|
||||
allpass_process(&enc->Filter1_WX[2], temp[1], temp[0], Filter1CoeffSqr[2], todo);
|
||||
allpass_process(&enc->Filter1_WX[3], temp[0], temp[1], Filter1CoeffSqr[3], todo);
|
||||
S[0] = enc->LastWX;
|
||||
for(i = 1;i < todo;i++)
|
||||
S[i] = temp[0][i-1];
|
||||
enc->LastWX = temp[0][i-1];
|
||||
|
||||
/* Left = (S + D)/2.0 */
|
||||
for(i = 0;i < todo;i++)
|
||||
*(LeftOut++) += (S[i] + D[i]) * 0.5f;
|
||||
/* Right = (S - D)/2.0 */
|
||||
for(i = 0;i < todo;i++)
|
||||
*(RightOut++) += (S[i] - D[i]) * 0.5f;
|
||||
|
||||
base += todo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef UHJFILTER_H
|
||||
#define UHJFILTER_H
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
typedef struct AllPassState {
|
||||
ALfloat z[2];
|
||||
} AllPassState;
|
||||
|
||||
/* Encoding 2-channel UHJ from B-Format is done as:
|
||||
*
|
||||
* S = 0.9396926*W + 0.1855740*X
|
||||
* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y
|
||||
*
|
||||
* Left = (S + D)/2.0
|
||||
* Right = (S - D)/2.0
|
||||
*
|
||||
* where j is a wide-band +90 degree phase shift.
|
||||
*
|
||||
* The phase shift is done using a Hilbert transform, described here:
|
||||
* https://web.archive.org/web/20060708031958/http://www.biochem.oulu.fi/~oniemita/dsp/hilbert/
|
||||
* It works using 2 sets of 4 chained filters. The first filter chain produces
|
||||
* a phase shift of varying magnitude over a wide range of frequencies, while
|
||||
* the second filter chain produces a phase shift 90 degrees ahead of the
|
||||
* first over the same range.
|
||||
*
|
||||
* Combining these two stages requires the use of three filter chains. S-
|
||||
* channel output uses a Filter1 chain on the W and X channel mix, while the D-
|
||||
* channel output uses a Filter1 chain on the Y channel plus a Filter2 chain on
|
||||
* the W and X channel mix. This results in the W and X input mix on the D-
|
||||
* channel output having the required +90 degree phase shift relative to the
|
||||
* other inputs.
|
||||
*/
|
||||
|
||||
typedef struct Uhj2Encoder {
|
||||
AllPassState Filter1_Y[4];
|
||||
AllPassState Filter2_WX[4];
|
||||
AllPassState Filter1_WX[4];
|
||||
ALfloat LastY, LastWX;
|
||||
} Uhj2Encoder;
|
||||
|
||||
/* Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input
|
||||
* signal. The input must use FuMa channel ordering and scaling.
|
||||
*/
|
||||
void EncodeUhj2(Uhj2Encoder *enc, ALfloat *restrict LeftOut, ALfloat *restrict RightOut, ALfloat (*restrict InSamples)[BUFFERSIZE], ALsizei SamplesToDo);
|
||||
|
||||
#endif /* UHJFILTER_H */
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef AL_VECTOR_H
|
||||
#define AL_VECTOR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
#define TYPEDEF_VECTOR(T, N) typedef struct { \
|
||||
size_t Capacity; \
|
||||
size_t Size; \
|
||||
T Data[]; \
|
||||
} _##N; \
|
||||
typedef _##N* N; \
|
||||
typedef const _##N* const_##N;
|
||||
|
||||
#define VECTOR(T) struct { \
|
||||
size_t Capacity; \
|
||||
size_t Size; \
|
||||
T Data[]; \
|
||||
}*
|
||||
|
||||
#define VECTOR_INIT(_x) do { (_x) = NULL; } while(0)
|
||||
#define VECTOR_INIT_STATIC() NULL
|
||||
#define VECTOR_DEINIT(_x) do { al_free((_x)); (_x) = NULL; } while(0)
|
||||
|
||||
#define VECTOR_RESIZE(_x, _s, _c) do { \
|
||||
size_t _size = (_s); \
|
||||
size_t _cap = (_c); \
|
||||
if(_size > _cap) \
|
||||
_cap = _size; \
|
||||
\
|
||||
if(!(_x) && _cap == 0) \
|
||||
break; \
|
||||
\
|
||||
if(((_x) ? (_x)->Capacity : 0) < _cap) \
|
||||
{ \
|
||||
ptrdiff_t data_offset = (_x) ? (char*)((_x)->Data) - (char*)(_x) : \
|
||||
sizeof(*(_x)); \
|
||||
size_t old_size = ((_x) ? (_x)->Size : 0); \
|
||||
void *temp; \
|
||||
\
|
||||
temp = al_calloc(16, data_offset + sizeof((_x)->Data[0])*_cap); \
|
||||
assert(temp != NULL); \
|
||||
if((_x)) \
|
||||
memcpy(((char*)temp)+data_offset, (_x)->Data, \
|
||||
sizeof((_x)->Data[0])*old_size); \
|
||||
\
|
||||
al_free((_x)); \
|
||||
(_x) = temp; \
|
||||
(_x)->Capacity = _cap; \
|
||||
} \
|
||||
(_x)->Size = _size; \
|
||||
} while(0) \
|
||||
|
||||
#define VECTOR_CAPACITY(_x) ((_x) ? (_x)->Capacity : 0)
|
||||
#define VECTOR_SIZE(_x) ((_x) ? (_x)->Size : 0)
|
||||
|
||||
#define VECTOR_BEGIN(_x) ((_x) ? (_x)->Data + 0 : NULL)
|
||||
#define VECTOR_END(_x) ((_x) ? (_x)->Data + (_x)->Size : NULL)
|
||||
|
||||
#define VECTOR_PUSH_BACK(_x, _obj) do { \
|
||||
size_t _pbsize = VECTOR_SIZE(_x)+1; \
|
||||
VECTOR_RESIZE(_x, _pbsize, _pbsize); \
|
||||
(_x)->Data[(_x)->Size-1] = (_obj); \
|
||||
} while(0)
|
||||
#define VECTOR_POP_BACK(_x) ((void)((_x)->Size--))
|
||||
|
||||
#define VECTOR_BACK(_x) ((_x)->Data[(_x)->Size-1])
|
||||
#define VECTOR_FRONT(_x) ((_x)->Data[0])
|
||||
|
||||
#define VECTOR_ELEM(_x, _o) ((_x)->Data[(_o)])
|
||||
|
||||
#define VECTOR_FOR_EACH(_t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
_f(_iter); \
|
||||
} while(0)
|
||||
|
||||
#define VECTOR_FIND_IF(_i, _t, _x, _f) do { \
|
||||
_t *_iter = VECTOR_BEGIN((_x)); \
|
||||
_t *_end = VECTOR_END((_x)); \
|
||||
for(;_iter != _end;++_iter) \
|
||||
{ \
|
||||
if(_f(_iter)) \
|
||||
break; \
|
||||
} \
|
||||
(_i) = _iter; \
|
||||
} while(0)
|
||||
|
||||
#endif /* AL_VECTOR_H */
|
||||
+1357
-370
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,15 @@
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is
|
||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -53,7 +51,7 @@ library. If the library is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original
|
||||
version, so that any problems introduced by others will not reflect on
|
||||
the original authors' reputations.
|
||||
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that companies distributing free
|
||||
software will individually obtain patent licenses, thus in effect
|
||||
@@ -100,8 +98,8 @@ works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary
|
||||
General Public License rather than by this special one.
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which
|
||||
@@ -147,7 +145,7 @@ Library.
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
@@ -205,7 +203,7 @@ instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
@@ -256,7 +254,7 @@ Library will still fall under Section 6.)
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
@@ -310,7 +308,7 @@ restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
@@ -351,7 +349,7 @@ subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
@@ -403,7 +401,7 @@ conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
@@ -413,7 +411,7 @@ decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
@@ -436,49 +434,4 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
openal-soft-1.19.1:
|
||||
|
||||
Implemented capture support for the SoundIO backend.
|
||||
|
||||
Fixed source buffer queues potentially not playing properly when a queue
|
||||
entry completes.
|
||||
|
||||
Fixed possible unexpected failures when generating auxiliary effect slots.
|
||||
|
||||
Fixed a crash with certain reverb or device settings.
|
||||
|
||||
Fixed OpenSL capture.
|
||||
|
||||
Improved output limiter response, better ensuring the sample amplitude is
|
||||
clamped for output.
|
||||
|
||||
openal-soft-1.19.0:
|
||||
|
||||
Implemented the ALC_SOFT_device_clock extension.
|
||||
|
||||
Implemented the Pitch Shifter, Frequency Shifter, and Autowah effects.
|
||||
|
||||
Fixed compiling on FreeBSD systems that use freebsd-lib 9.1.
|
||||
|
||||
Fixed compiling on NetBSD.
|
||||
|
||||
Fixed the reverb effect's density scale and panning parameters.
|
||||
|
||||
Fixed use of the WASAPI backend with certain games, which caused odd COM
|
||||
initialization errors.
|
||||
|
||||
Increased the number of virtual channels for decoding Ambisonics to HRTF
|
||||
output.
|
||||
|
||||
Changed 32-bit x86 builds to use SSE2 math by default for performance.
|
||||
Build-time options are available to use just SSE1 or x87 instead.
|
||||
|
||||
Replaced the 4-point Sinc resampler with a more efficient cubic resampler.
|
||||
|
||||
Renamed the MMDevAPI backend to WASAPI.
|
||||
|
||||
Added support for 24-bit, dual-ear HRTF data sets. The built-in data set
|
||||
has been updated to 24-bit.
|
||||
|
||||
Added a 24- to 48-point band-limited Sinc resampler.
|
||||
|
||||
Added an SDL2 playback backend. Disabled by default to avoid a dependency
|
||||
on SDL2.
|
||||
|
||||
Improved the performance and quality of the Chorus and Flanger effects.
|
||||
|
||||
Improved the efficiency of the band-limited Sinc resampler.
|
||||
|
||||
Improved the Sinc resampler's transition band to avoid over-attenuating
|
||||
higher frequencies.
|
||||
|
||||
Improved the performance of some filter operations.
|
||||
|
||||
Improved the efficiency of object ID lookups.
|
||||
|
||||
Improved the efficienty of internal voice/source synchronization.
|
||||
|
||||
Improved AL call error logging with contextualized messages.
|
||||
|
||||
Removed the reverb effect's modulation stage. Due to the lack of reference
|
||||
for its intended behavior and strength.
|
||||
|
||||
openal-soft-1.18.2:
|
||||
|
||||
Fixed resetting the FPU rounding mode after certain function calls on
|
||||
Windows.
|
||||
|
||||
Fixed use of SSE intrinsics when building with Clang on Windows.
|
||||
|
||||
Fixed a crash with the JACK backend when using JACK1.
|
||||
|
||||
Fixed use of pthread_setnane_np on NetBSD.
|
||||
|
||||
Fixed building on FreeBSD with an older freebsd-lib.
|
||||
|
||||
OSS now links with libossaudio if found at build time (for NetBSD).
|
||||
|
||||
openal-soft-1.18.1:
|
||||
|
||||
Fixed an issue where resuming a source might not restart playing it.
|
||||
|
||||
Fixed PulseAudio playback when the configured stream length is much less
|
||||
than the requested length.
|
||||
|
||||
Fixed MMDevAPI capture with sample rates not matching the backing device.
|
||||
|
||||
Fixed int32 output for the Wave Writer.
|
||||
|
||||
Fixed enumeration of OSS devices that are missing device files.
|
||||
|
||||
Added correct retrieval of the executable's path on FreeBSD.
|
||||
|
||||
Added a config option to specify the dithering depth.
|
||||
|
||||
Added a 5.1 decoder preset that excludes front-center output.
|
||||
|
||||
openal-soft-1.18.0:
|
||||
|
||||
Implemented the AL_EXT_STEREO_ANGLES and AL_EXT_SOURCE_RADIUS extensions.
|
||||
|
||||
Implemented the AL_SOFT_gain_clamp_ex, AL_SOFT_source_resampler,
|
||||
AL_SOFT_source_spatialize, and ALC_SOFT_output_limiter extensions.
|
||||
|
||||
Implemented 3D processing for some effects. Currently implemented for
|
||||
Reverb, Compressor, Equalizer, and Ring Modulator.
|
||||
|
||||
Implemented 2-channel UHJ output encoding. This needs to be enabled with a
|
||||
config option to be used.
|
||||
|
||||
Implemented dual-band processing for high-quality ambisonic decoding.
|
||||
|
||||
Implemented distance-compensation for surround sound output.
|
||||
|
||||
Implemented near-field emulation and compensation with ambisonic rendering.
|
||||
Currently only applies when using the high-quality ambisonic decoder or
|
||||
ambisonic output, with appropriate config options.
|
||||
|
||||
Implemented an output limiter to reduce the amount of distortion from
|
||||
clipping.
|
||||
|
||||
Implemented dithering for 8-bit and 16-bit output.
|
||||
|
||||
Implemented a config option to select a preferred HRTF.
|
||||
|
||||
Implemented a run-time check for NEON extensions using /proc/cpuinfo.
|
||||
|
||||
Implemented experimental capture support for the OpenSL backend.
|
||||
|
||||
Fixed building on compilers with NEON support but don't default to having
|
||||
NEON enabled.
|
||||
|
||||
Fixed support for JACK on Windows.
|
||||
|
||||
Fixed starting a source while alcSuspendContext is in effect.
|
||||
|
||||
Fixed detection of headsets as headphones, with MMDevAPI.
|
||||
|
||||
Added support for AmbDec config files, for custom ambisonic decoder
|
||||
configurations. Version 3 files only.
|
||||
|
||||
Added backend-specific options to alsoft-config.
|
||||
|
||||
Added first-, second-, and third-order ambisonic output formats. Currently
|
||||
only works with backends that don't rely on channel labels, like JACK,
|
||||
ALSA, and OSS.
|
||||
|
||||
Added a build option to embed the default HRTFs into the lib.
|
||||
|
||||
Added AmbDec presets to enable high-quality ambisonic decoding.
|
||||
|
||||
Added an AmbDec preset for 3D7.1 speaker setups.
|
||||
|
||||
Added documentation regarding Ambisonics, 3D7.1, AmbDec config files, and
|
||||
the provided ambdec presets.
|
||||
|
||||
Added the ability for MMDevAPI to open devices given a Device ID or GUID
|
||||
string.
|
||||
|
||||
Added an option to the example apps to open a specific device.
|
||||
|
||||
Increased the maximum auxiliary send limit to 16 (up from 4). Requires
|
||||
requesting them with the ALC_MAX_AUXILIARY_SENDS context creation
|
||||
attribute.
|
||||
|
||||
Increased the default auxiliary effect slot count to 64 (up from 4).
|
||||
|
||||
Reduced the default period count to 3 (down from 4).
|
||||
|
||||
Slightly improved automatic naming for enumerated HRTFs.
|
||||
|
||||
Improved B-Format decoding with HRTF output.
|
||||
|
||||
Improved internal property handling for better batching behavior.
|
||||
|
||||
Improved performance of certain filter uses.
|
||||
|
||||
Removed support for the AL_SOFT_buffer_samples and AL_SOFT_buffer_sub_data
|
||||
extensions. Due to conflicts with AL_EXT_SOURCE_RADIUS.
|
||||
|
||||
openal-soft-1.17.2:
|
||||
|
||||
Implemented device enumeration for OSSv4.
|
||||
|
||||
Fixed building on OSX.
|
||||
|
||||
Fixed building on non-Windows systems without POSIX-2008.
|
||||
|
||||
Fixed Dedicated Dialog and Dedicated LFE effect output.
|
||||
|
||||
Added a build option to override the share install dir.
|
||||
|
||||
Added a build option to static-link libgcc for MinGW.
|
||||
|
||||
openal-soft-1.17.1:
|
||||
|
||||
Fixed building with JACK and without PulseAudio.
|
||||
|
||||
Fixed building on FreeBSD.
|
||||
|
||||
Fixed the ALSA backend's allow-resampler option.
|
||||
|
||||
Fixed handling of inexact ALSA period counts.
|
||||
|
||||
Altered device naming scheme on Windows backends to better match other
|
||||
drivers.
|
||||
|
||||
Updated the CoreAudio backend to use the AudioComponent API. This clears up
|
||||
deprecation warnings for OSX 10.11, although requires OSX 10.6 or newer.
|
||||
|
||||
openal-soft-1.17.0:
|
||||
|
||||
Implemented a JACK playback backend.
|
||||
|
||||
Implemented the AL_EXT_BFORMAT and AL_EXT_MULAW_BFORMAT extensions.
|
||||
|
||||
Implemented the ALC_SOFT_HRTF extension.
|
||||
|
||||
Implemented C, SSE3, and SSE4.1 based 4- and 8-point Sinc resamplers.
|
||||
|
||||
Implemented a C and SSE based band-limited Sinc resampler. This does 12- to
|
||||
24-point Sinc resampling, and performs anti-aliasing.
|
||||
|
||||
Implemented B-Format output support for the wave file writer. This creates
|
||||
FuMa-style first-order Ambisonics wave files (AMB format).
|
||||
|
||||
Implemented a stereo-mode config option for treating stereo modes as either
|
||||
speakers or headphones.
|
||||
|
||||
Implemented per-device configuration options.
|
||||
|
||||
Fixed handling of PulseAudio and MMDevAPI devices that have identical
|
||||
descriptions.
|
||||
|
||||
Fixed a potential lockup when stopping playback of suspended PulseAudio devices.
|
||||
|
||||
Fixed logging of Unicode characters on Windows.
|
||||
|
||||
Fixed 5.1 surround sound channels. By default it will now use the side
|
||||
channels for the surround output. A configuration using rear channels is
|
||||
still available.
|
||||
|
||||
Fixed the QSA backend potentially altering the capture format.
|
||||
|
||||
Fixed detecting MMDevAPI's default device.
|
||||
|
||||
Fixed returning the default capture device name.
|
||||
|
||||
Fixed mixing property calculations when deferring context updates.
|
||||
|
||||
Altered the behavior of alcSuspendContext and alcProcessContext to better
|
||||
match certain Windows drivers.
|
||||
|
||||
Altered the panning algorithm, utilizing Ambisonics for better side and
|
||||
back positioning cues with surround sound output.
|
||||
|
||||
Improved support for certain older Windows apps.
|
||||
|
||||
Improved the alffplay example to support surround sound streams.
|
||||
|
||||
Improved support for building as a sub-project.
|
||||
|
||||
Added an HRTF playback example.
|
||||
|
||||
Added a tone generator output test.
|
||||
|
||||
Added a toolchain to help with cross-compiling to Android.
|
||||
|
||||
openal-soft-1.16.0:
|
||||
|
||||
Implemented EFX Chorus, Flanger, Distortion, Equalizer, and Compressor
|
||||
effects.
|
||||
|
||||
Implemented high-pass and band-pass EFX filters.
|
||||
|
||||
Implemented the high-pass filter for the EAXReverb effect.
|
||||
|
||||
Implemented SSE2 and SSE4.1 linear resamplers.
|
||||
|
||||
Implemented Neon-enhanced non-HRTF mixers.
|
||||
|
||||
Implemented a QSA backend, for QNX.
|
||||
|
||||
Implemented the ALC_SOFT_pause_device, AL_SOFT_deferred_updates,
|
||||
AL_SOFT_block_alignment, AL_SOFT_MSADPCM, and AL_SOFT_source_length
|
||||
extensions.
|
||||
|
||||
Fixed resetting mmdevapi backend devices.
|
||||
|
||||
Fixed clamping when converting 32-bit float samples to integer.
|
||||
|
||||
Fixed modulation range in the Modulator effect.
|
||||
|
||||
Several fixes for the OpenSL playback backend.
|
||||
|
||||
Fixed device specifier names that have Unicode characters on Windows.
|
||||
|
||||
Added support for filenames and paths with Unicode (UTF-8) characters on
|
||||
Windows.
|
||||
|
||||
Added support for alsoft.conf config files found in XDG Base Directory
|
||||
Specification locations (XDG_CONFIG_DIRS and XDG_CONFIG_HOME, or their
|
||||
defaults) on non-Windows systems.
|
||||
|
||||
Added a GUI configuration utility (requires Qt 4.8).
|
||||
|
||||
Added support for environment variable expansion in config options (not
|
||||
keys or section names).
|
||||
|
||||
Added an example that uses SDL2 and ffmpeg.
|
||||
|
||||
Modified examples to use SDL_sound.
|
||||
|
||||
Modified CMake config option names for better sorting.
|
||||
|
||||
HRTF data sets specified in the hrtf_tables config option may now be
|
||||
relative or absolute filenames.
|
||||
|
||||
Made the default HRTF data set an external file, and added a data set for
|
||||
48khz playback in addition to 44.1khz.
|
||||
|
||||
Added support for C11 atomic methods.
|
||||
|
||||
Improved support for some non-GNU build systems.
|
||||
|
||||
openal-soft-1.15.1:
|
||||
|
||||
Fixed a regression with retrieving the source's AL_GAIN property.
|
||||
|
||||
openal-soft-1.15:
|
||||
|
||||
Fixed device enumeration with the OSS backend.
|
||||
|
||||
Reorganized internal mixing logic, so unneeded steps can potentially be
|
||||
skipped for better performance.
|
||||
|
||||
Removed the lookup table for calculating the mixing pans. The panning is
|
||||
now calculated directly for better precision.
|
||||
|
||||
Improved the panning of stereo source channels when using stereo output.
|
||||
|
||||
Improved source filter quality on send paths.
|
||||
|
||||
Added a config option to allow PulseAudio to move streams between devices.
|
||||
|
||||
The PulseAudio backend will now attempt to spawn a server by default.
|
||||
|
||||
Added a workaround for a DirectSound bug relating to float32 output.
|
||||
|
||||
Added SSE-based mixers, for HRTF and non-HRTF mixing.
|
||||
|
||||
Added support for the new AL_SOFT_source_latency extension.
|
||||
|
||||
Improved ALSA capture by avoiding an extra buffer when using sizes
|
||||
supported by the underlying device.
|
||||
|
||||
Improved the makehrtf utility to support new options and input formats.
|
||||
|
||||
Modified the CFLAGS declared in the pkg-config file so the "AL/" portion of
|
||||
the header includes can optionally be omitted.
|
||||
|
||||
Added a couple example code programs to show how to apply reverb, and
|
||||
retrieve latency.
|
||||
|
||||
The configuration sample is now installed into the share/openal/ directory
|
||||
instead of /etc/openal.
|
||||
|
||||
The configuration sample now gets installed by default.
|
||||
@@ -1,62 +1,182 @@
|
||||
#ifndef _AL_AUXEFFECTSLOT_H_
|
||||
#define _AL_AUXEFFECTSLOT_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
#include "alEffect.h"
|
||||
#include "alFilter.h"
|
||||
|
||||
#include "atomic.h"
|
||||
#include "align.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALeffectState ALeffectState;
|
||||
struct ALeffectStateVtable;
|
||||
struct ALeffectslot;
|
||||
|
||||
typedef struct ALeffectslot
|
||||
{
|
||||
ALeffect effect;
|
||||
typedef struct ALeffectState {
|
||||
RefCount Ref;
|
||||
const struct ALeffectStateVtable *vtbl;
|
||||
|
||||
volatile ALfloat Gain;
|
||||
volatile ALboolean AuxSendAuto;
|
||||
ALfloat (*OutBuffer)[BUFFERSIZE];
|
||||
ALsizei OutChannels;
|
||||
} ALeffectState;
|
||||
|
||||
volatile ALenum NeedsUpdate;
|
||||
ALeffectState *EffectState;
|
||||
void ALeffectState_Construct(ALeffectState *state);
|
||||
void ALeffectState_Destruct(ALeffectState *state);
|
||||
|
||||
ALfloat WetBuffer[BUFFERSIZE];
|
||||
struct ALeffectStateVtable {
|
||||
void (*const Destruct)(ALeffectState *state);
|
||||
|
||||
ALfloat ClickRemoval[1];
|
||||
ALfloat PendingClicks[1];
|
||||
ALboolean (*const deviceUpdate)(ALeffectState *state, ALCdevice *device);
|
||||
void (*const update)(ALeffectState *state, const ALCcontext *context, const struct ALeffectslot *slot, const union ALeffectProps *props);
|
||||
void (*const process)(ALeffectState *state, ALsizei samplesToDo, const ALfloat (*restrict samplesIn)[BUFFERSIZE], ALfloat (*restrict samplesOut)[BUFFERSIZE], ALsizei numChannels);
|
||||
|
||||
void (*const Delete)(void *ptr);
|
||||
};
|
||||
|
||||
/* Small hack to use a pointer-to-array types as a normal argument type.
|
||||
* Shouldn't be used directly.
|
||||
*/
|
||||
typedef ALfloat ALfloatBUFFERSIZE[BUFFERSIZE];
|
||||
|
||||
#define DEFINE_ALEFFECTSTATE_VTABLE(T) \
|
||||
DECLARE_THUNK(T, ALeffectState, void, Destruct) \
|
||||
DECLARE_THUNK1(T, ALeffectState, ALboolean, deviceUpdate, ALCdevice*) \
|
||||
DECLARE_THUNK3(T, ALeffectState, void, update, const ALCcontext*, const ALeffectslot*, const ALeffectProps*) \
|
||||
DECLARE_THUNK4(T, ALeffectState, void, process, ALsizei, const ALfloatBUFFERSIZE*restrict, ALfloatBUFFERSIZE*restrict, ALsizei) \
|
||||
static void T##_ALeffectState_Delete(void *ptr) \
|
||||
{ return T##_Delete(STATIC_UPCAST(T, ALeffectState, (ALeffectState*)ptr)); } \
|
||||
\
|
||||
static const struct ALeffectStateVtable T##_ALeffectState_vtable = { \
|
||||
T##_ALeffectState_Destruct, \
|
||||
\
|
||||
T##_ALeffectState_deviceUpdate, \
|
||||
T##_ALeffectState_update, \
|
||||
T##_ALeffectState_process, \
|
||||
\
|
||||
T##_ALeffectState_Delete, \
|
||||
}
|
||||
|
||||
|
||||
struct EffectStateFactoryVtable;
|
||||
|
||||
typedef struct EffectStateFactory {
|
||||
const struct EffectStateFactoryVtable *vtab;
|
||||
} EffectStateFactory;
|
||||
|
||||
struct EffectStateFactoryVtable {
|
||||
ALeffectState *(*const create)(EffectStateFactory *factory);
|
||||
};
|
||||
#define EffectStateFactory_create(x) ((x)->vtab->create((x)))
|
||||
|
||||
#define DEFINE_EFFECTSTATEFACTORY_VTABLE(T) \
|
||||
DECLARE_THUNK(T, EffectStateFactory, ALeffectState*, create) \
|
||||
\
|
||||
static const struct EffectStateFactoryVtable T##_EffectStateFactory_vtable = { \
|
||||
T##_EffectStateFactory_create, \
|
||||
}
|
||||
|
||||
|
||||
#define MAX_EFFECT_CHANNELS (4)
|
||||
|
||||
|
||||
struct ALeffectslotArray {
|
||||
ALsizei count;
|
||||
struct ALeffectslot *slot[];
|
||||
};
|
||||
|
||||
|
||||
struct ALeffectslotProps {
|
||||
ALfloat Gain;
|
||||
ALboolean AuxSendAuto;
|
||||
|
||||
ALenum Type;
|
||||
ALeffectProps Props;
|
||||
|
||||
ALeffectState *State;
|
||||
|
||||
ATOMIC(struct ALeffectslotProps*) next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ALeffectslot {
|
||||
ALfloat Gain;
|
||||
ALboolean AuxSendAuto;
|
||||
|
||||
struct {
|
||||
ALenum Type;
|
||||
ALeffectProps Props;
|
||||
|
||||
ALeffectState *State;
|
||||
} Effect;
|
||||
|
||||
ATOMIC_FLAG PropsClean;
|
||||
|
||||
RefCount ref;
|
||||
|
||||
// Index to itself
|
||||
ALuint effectslot;
|
||||
ATOMIC(struct ALeffectslotProps*) Update;
|
||||
|
||||
struct ALeffectslot *next;
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
ALboolean AuxSendAuto;
|
||||
|
||||
ALenum EffectType;
|
||||
ALeffectProps EffectProps;
|
||||
ALeffectState *EffectState;
|
||||
|
||||
ALfloat RoomRolloff; /* Added to the source's room rolloff, not multiplied. */
|
||||
ALfloat DecayTime;
|
||||
ALfloat DecayLFRatio;
|
||||
ALfloat DecayHFRatio;
|
||||
ALboolean DecayHFLimit;
|
||||
ALfloat AirAbsorptionGainHF;
|
||||
} Params;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
|
||||
ALsizei NumChannels;
|
||||
BFChannelConfig ChanMap[MAX_EFFECT_CHANNELS];
|
||||
/* Wet buffer configuration is ACN channel order with N3D scaling:
|
||||
* * Channel 0 is the unattenuated mono signal.
|
||||
* * Channel 1 is OpenAL -X * sqrt(3)
|
||||
* * Channel 2 is OpenAL Y * sqrt(3)
|
||||
* * Channel 3 is OpenAL -Z * sqrt(3)
|
||||
* Consequently, effects that only want to work with mono input can use
|
||||
* channel 0 by itself. Effects that want multichannel can process the
|
||||
* ambisonics signal and make a B-Format source pan for first-order device
|
||||
* output (FOAOut).
|
||||
*/
|
||||
alignas(16) ALfloat WetBuffer[MAX_EFFECT_CHANNELS][BUFFERSIZE];
|
||||
} ALeffectslot;
|
||||
|
||||
|
||||
ALenum InitEffectSlot(ALeffectslot *slot);
|
||||
void DeinitEffectSlot(ALeffectslot *slot);
|
||||
void UpdateEffectSlotProps(ALeffectslot *slot, ALCcontext *context);
|
||||
void UpdateAllEffectSlotProps(ALCcontext *context);
|
||||
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context);
|
||||
|
||||
struct ALeffectState {
|
||||
ALvoid (*Destroy)(ALeffectState *State);
|
||||
ALboolean (*DeviceUpdate)(ALeffectState *State, ALCdevice *Device);
|
||||
ALvoid (*Update)(ALeffectState *State, ALCdevice *Device, const ALeffectslot *Slot);
|
||||
ALvoid (*Process)(ALeffectState *State, ALuint SamplesToDo, const ALfloat *SamplesIn, ALfloat (*SamplesOut)[MAXCHANNELS]);
|
||||
};
|
||||
|
||||
ALeffectState *NoneCreate(void);
|
||||
ALeffectState *ReverbCreate(void);
|
||||
ALeffectState *EchoCreate(void);
|
||||
ALeffectState *ModulatorCreate(void);
|
||||
ALeffectState *DedicatedCreate(void);
|
||||
EffectStateFactory *NullStateFactory_getFactory(void);
|
||||
EffectStateFactory *ReverbStateFactory_getFactory(void);
|
||||
EffectStateFactory *AutowahStateFactory_getFactory(void);
|
||||
EffectStateFactory *ChorusStateFactory_getFactory(void);
|
||||
EffectStateFactory *CompressorStateFactory_getFactory(void);
|
||||
EffectStateFactory *DistortionStateFactory_getFactory(void);
|
||||
EffectStateFactory *EchoStateFactory_getFactory(void);
|
||||
EffectStateFactory *EqualizerStateFactory_getFactory(void);
|
||||
EffectStateFactory *FlangerStateFactory_getFactory(void);
|
||||
EffectStateFactory *FshifterStateFactory_getFactory(void);
|
||||
EffectStateFactory *ModulatorStateFactory_getFactory(void);
|
||||
EffectStateFactory *PshifterStateFactory_getFactory(void);
|
||||
|
||||
#define ALeffectState_Destroy(a) ((a)->Destroy((a)))
|
||||
#define ALeffectState_DeviceUpdate(a,b) ((a)->DeviceUpdate((a),(b)))
|
||||
#define ALeffectState_Update(a,b,c) ((a)->Update((a),(b),(c)))
|
||||
#define ALeffectState_Process(a,b,c,d) ((a)->Process((a),(b),(c),(d)))
|
||||
EffectStateFactory *DedicatedStateFactory_getFactory(void);
|
||||
|
||||
ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect);
|
||||
|
||||
ALenum InitializeEffect(ALCcontext *Context, ALeffectslot *EffectSlot, ALeffect *effect);
|
||||
|
||||
void ALeffectState_DecRef(ALeffectState *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+55
-41
@@ -1,7 +1,13 @@
|
||||
#ifndef _AL_BUFFER_H_
|
||||
#define _AL_BUFFER_H_
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "inprogext.h"
|
||||
#include "atomic.h"
|
||||
#include "rwlock.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -9,34 +15,30 @@ extern "C" {
|
||||
|
||||
/* User formats */
|
||||
enum UserFmtType {
|
||||
UserFmtByte = AL_BYTE_SOFT,
|
||||
UserFmtUByte = AL_UNSIGNED_BYTE_SOFT,
|
||||
UserFmtShort = AL_SHORT_SOFT,
|
||||
UserFmtUShort = AL_UNSIGNED_SHORT_SOFT,
|
||||
UserFmtInt = AL_INT_SOFT,
|
||||
UserFmtUInt = AL_UNSIGNED_INT_SOFT,
|
||||
UserFmtFloat = AL_FLOAT_SOFT,
|
||||
UserFmtDouble = AL_DOUBLE_SOFT,
|
||||
UserFmtByte3 = AL_BYTE3_SOFT,
|
||||
UserFmtUByte3 = AL_UNSIGNED_BYTE3_SOFT,
|
||||
UserFmtUByte,
|
||||
UserFmtShort,
|
||||
UserFmtFloat,
|
||||
UserFmtDouble,
|
||||
UserFmtMulaw,
|
||||
UserFmtAlaw,
|
||||
UserFmtIMA4,
|
||||
UserFmtMSADPCM,
|
||||
};
|
||||
enum UserFmtChannels {
|
||||
UserFmtMono = AL_MONO_SOFT,
|
||||
UserFmtStereo = AL_STEREO_SOFT,
|
||||
UserFmtRear = AL_REAR_SOFT,
|
||||
UserFmtQuad = AL_QUAD_SOFT,
|
||||
UserFmtX51 = AL_5POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtX61 = AL_6POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtX71 = AL_7POINT1_SOFT, /* (WFX order) */
|
||||
UserFmtMono,
|
||||
UserFmtStereo,
|
||||
UserFmtRear,
|
||||
UserFmtQuad,
|
||||
UserFmtX51, /* (WFX order) */
|
||||
UserFmtX61, /* (WFX order) */
|
||||
UserFmtX71, /* (WFX order) */
|
||||
UserFmtBFormat2D, /* WXY */
|
||||
UserFmtBFormat3D, /* WXYZ */
|
||||
};
|
||||
|
||||
ALuint BytesFromUserFmt(enum UserFmtType type);
|
||||
ALuint ChannelsFromUserFmt(enum UserFmtChannels chans);
|
||||
static __inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans,
|
||||
enum UserFmtType type)
|
||||
ALsizei BytesFromUserFmt(enum UserFmtType type);
|
||||
ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans);
|
||||
inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type)
|
||||
{
|
||||
return ChannelsFromUserFmt(chans) * BytesFromUserFmt(type);
|
||||
}
|
||||
@@ -44,9 +46,12 @@ static __inline ALuint FrameSizeFromUserFmt(enum UserFmtChannels chans,
|
||||
|
||||
/* Storable formats */
|
||||
enum FmtType {
|
||||
FmtByte = UserFmtByte,
|
||||
FmtShort = UserFmtShort,
|
||||
FmtFloat = UserFmtFloat,
|
||||
FmtUByte = UserFmtUByte,
|
||||
FmtShort = UserFmtShort,
|
||||
FmtFloat = UserFmtFloat,
|
||||
FmtDouble = UserFmtDouble,
|
||||
FmtMulaw = UserFmtMulaw,
|
||||
FmtAlaw = UserFmtAlaw,
|
||||
};
|
||||
enum FmtChannels {
|
||||
FmtMono = UserFmtMono,
|
||||
@@ -56,40 +61,49 @@ enum FmtChannels {
|
||||
FmtX51 = UserFmtX51,
|
||||
FmtX61 = UserFmtX61,
|
||||
FmtX71 = UserFmtX71,
|
||||
FmtBFormat2D = UserFmtBFormat2D,
|
||||
FmtBFormat3D = UserFmtBFormat3D,
|
||||
};
|
||||
#define MAX_INPUT_CHANNELS (8)
|
||||
|
||||
ALuint BytesFromFmt(enum FmtType type);
|
||||
ALuint ChannelsFromFmt(enum FmtChannels chans);
|
||||
static __inline ALuint FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
|
||||
ALsizei BytesFromFmt(enum FmtType type);
|
||||
ALsizei ChannelsFromFmt(enum FmtChannels chans);
|
||||
inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type)
|
||||
{
|
||||
return ChannelsFromFmt(chans) * BytesFromFmt(type);
|
||||
}
|
||||
|
||||
|
||||
typedef struct ALbuffer
|
||||
{
|
||||
typedef struct ALbuffer {
|
||||
ALvoid *data;
|
||||
|
||||
ALsizei Frequency;
|
||||
ALenum Format;
|
||||
ALsizei SampleLen;
|
||||
ALsizei Frequency;
|
||||
ALbitfieldSOFT Access;
|
||||
ALsizei SampleLen;
|
||||
|
||||
enum FmtChannels FmtChannels;
|
||||
enum FmtType FmtType;
|
||||
ALsizei BytesAlloc;
|
||||
|
||||
enum UserFmtChannels OriginalChannels;
|
||||
enum UserFmtType OriginalType;
|
||||
ALsizei OriginalSize;
|
||||
enum UserFmtType OriginalType;
|
||||
ALsizei OriginalSize;
|
||||
ALsizei OriginalAlign;
|
||||
|
||||
ALsizei LoopStart;
|
||||
ALsizei LoopEnd;
|
||||
ALsizei LoopStart;
|
||||
ALsizei LoopEnd;
|
||||
|
||||
RefCount ref; // Number of sources using this buffer (deletion can only occur when this is 0)
|
||||
ATOMIC(ALsizei) UnpackAlign;
|
||||
ATOMIC(ALsizei) PackAlign;
|
||||
|
||||
RWLock lock;
|
||||
ALbitfieldSOFT MappedAccess;
|
||||
ALsizei MappedOffset;
|
||||
ALsizei MappedSize;
|
||||
|
||||
// Index to itself
|
||||
ALuint buffer;
|
||||
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
||||
RefCount ref;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALbuffer;
|
||||
|
||||
ALvoid ReleaseALBuffers(ALCdevice *device);
|
||||
|
||||
+134
-35
@@ -1,31 +1,81 @@
|
||||
#ifndef _AL_EFFECT_H_
|
||||
#define _AL_EFFECT_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ALeffect;
|
||||
|
||||
enum {
|
||||
EAXREVERB = 0,
|
||||
REVERB,
|
||||
ECHO,
|
||||
MODULATOR,
|
||||
DEDICATED,
|
||||
EAXREVERB_EFFECT = 0,
|
||||
REVERB_EFFECT,
|
||||
AUTOWAH_EFFECT,
|
||||
CHORUS_EFFECT,
|
||||
COMPRESSOR_EFFECT,
|
||||
DISTORTION_EFFECT,
|
||||
ECHO_EFFECT,
|
||||
EQUALIZER_EFFECT,
|
||||
FLANGER_EFFECT,
|
||||
FSHIFTER_EFFECT,
|
||||
MODULATOR_EFFECT,
|
||||
PSHIFTER_EFFECT,
|
||||
DEDICATED_EFFECT,
|
||||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
extern ALboolean DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
extern ALfloat ReverbBoost;
|
||||
extern ALboolean EmulateEAXReverb;
|
||||
|
||||
typedef struct ALeffect
|
||||
{
|
||||
// Effect type (AL_EFFECT_NULL, ...)
|
||||
ALenum type;
|
||||
struct EffectList {
|
||||
const char name[16];
|
||||
int type;
|
||||
ALenum val;
|
||||
};
|
||||
#define EFFECTLIST_SIZE 14
|
||||
extern const struct EffectList EffectList[EFFECTLIST_SIZE];
|
||||
|
||||
|
||||
struct ALeffectVtable {
|
||||
void (*const setParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*const setParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*const setParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*const setParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
void (*const getParami)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*const getParamiv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*const getParamf)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*const getParamfv)(const struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
};
|
||||
|
||||
#define DEFINE_ALEFFECT_VTABLE(T) \
|
||||
const struct ALeffectVtable T##_vtable = { \
|
||||
T##_setParami, T##_setParamiv, \
|
||||
T##_setParamf, T##_setParamfv, \
|
||||
T##_getParami, T##_getParamiv, \
|
||||
T##_getParamf, T##_getParamfv, \
|
||||
}
|
||||
|
||||
extern const struct ALeffectVtable ALeaxreverb_vtable;
|
||||
extern const struct ALeffectVtable ALreverb_vtable;
|
||||
extern const struct ALeffectVtable ALautowah_vtable;
|
||||
extern const struct ALeffectVtable ALchorus_vtable;
|
||||
extern const struct ALeffectVtable ALcompressor_vtable;
|
||||
extern const struct ALeffectVtable ALdistortion_vtable;
|
||||
extern const struct ALeffectVtable ALecho_vtable;
|
||||
extern const struct ALeffectVtable ALequalizer_vtable;
|
||||
extern const struct ALeffectVtable ALflanger_vtable;
|
||||
extern const struct ALeffectVtable ALfshifter_vtable;
|
||||
extern const struct ALeffectVtable ALmodulator_vtable;
|
||||
extern const struct ALeffectVtable ALnull_vtable;
|
||||
extern const struct ALeffectVtable ALpshifter_vtable;
|
||||
extern const struct ALeffectVtable ALdedicated_vtable;
|
||||
|
||||
|
||||
typedef union ALeffectProps {
|
||||
struct {
|
||||
// Shared Reverb Properties
|
||||
ALfloat Density;
|
||||
@@ -55,6 +105,34 @@ typedef struct ALeffect
|
||||
ALfloat LFReference;
|
||||
} Reverb;
|
||||
|
||||
struct {
|
||||
ALfloat AttackTime;
|
||||
ALfloat ReleaseTime;
|
||||
ALfloat Resonance;
|
||||
ALfloat PeakGain;
|
||||
} Autowah;
|
||||
|
||||
struct {
|
||||
ALint Waveform;
|
||||
ALint Phase;
|
||||
ALfloat Rate;
|
||||
ALfloat Depth;
|
||||
ALfloat Feedback;
|
||||
ALfloat Delay;
|
||||
} Chorus; /* Also Flanger */
|
||||
|
||||
struct {
|
||||
ALboolean OnOff;
|
||||
} Compressor;
|
||||
|
||||
struct {
|
||||
ALfloat Edge;
|
||||
ALfloat Gain;
|
||||
ALfloat LowpassCutoff;
|
||||
ALfloat EQCenter;
|
||||
ALfloat EQBandwidth;
|
||||
} Distortion;
|
||||
|
||||
struct {
|
||||
ALfloat Delay;
|
||||
ALfloat LRDelay;
|
||||
@@ -65,47 +143,68 @@ typedef struct ALeffect
|
||||
ALfloat Spread;
|
||||
} Echo;
|
||||
|
||||
struct {
|
||||
ALfloat LowCutoff;
|
||||
ALfloat LowGain;
|
||||
ALfloat Mid1Center;
|
||||
ALfloat Mid1Gain;
|
||||
ALfloat Mid1Width;
|
||||
ALfloat Mid2Center;
|
||||
ALfloat Mid2Gain;
|
||||
ALfloat Mid2Width;
|
||||
ALfloat HighCutoff;
|
||||
ALfloat HighGain;
|
||||
} Equalizer;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALint LeftDirection;
|
||||
ALint RightDirection;
|
||||
} Fshifter;
|
||||
|
||||
struct {
|
||||
ALfloat Frequency;
|
||||
ALfloat HighPassCutoff;
|
||||
ALint Waveform;
|
||||
} Modulator;
|
||||
|
||||
struct {
|
||||
ALint CoarseTune;
|
||||
ALint FineTune;
|
||||
} Pshifter;
|
||||
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
} Dedicated;
|
||||
} ALeffectProps;
|
||||
|
||||
void (*SetParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*SetParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*SetParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*SetParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
typedef struct ALeffect {
|
||||
// Effect type (AL_EFFECT_NULL, ...)
|
||||
ALenum type;
|
||||
|
||||
void (*GetParami)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*GetParamiv)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*GetParamf)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*GetParamfv)(struct ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
ALeffectProps Props;
|
||||
|
||||
// Index to itself
|
||||
ALuint effect;
|
||||
const struct ALeffectVtable *vtab;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALeffect;
|
||||
#define ALeffect_setParami(o, c, p, v) ((o)->vtab->setParami(o, c, p, v))
|
||||
#define ALeffect_setParamf(o, c, p, v) ((o)->vtab->setParamf(o, c, p, v))
|
||||
#define ALeffect_setParamiv(o, c, p, v) ((o)->vtab->setParamiv(o, c, p, v))
|
||||
#define ALeffect_setParamfv(o, c, p, v) ((o)->vtab->setParamfv(o, c, p, v))
|
||||
#define ALeffect_getParami(o, c, p, v) ((o)->vtab->getParami(o, c, p, v))
|
||||
#define ALeffect_getParamf(o, c, p, v) ((o)->vtab->getParamf(o, c, p, v))
|
||||
#define ALeffect_getParamiv(o, c, p, v) ((o)->vtab->getParamiv(o, c, p, v))
|
||||
#define ALeffect_getParamfv(o, c, p, v) ((o)->vtab->getParamfv(o, c, p, v))
|
||||
|
||||
#define ALeffect_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
|
||||
#define ALeffect_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
|
||||
#define ALeffect_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
|
||||
#define ALeffect_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
|
||||
|
||||
#define ALeffect_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
|
||||
#define ALeffect_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
|
||||
#define ALeffect_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
|
||||
#define ALeffect_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
|
||||
|
||||
static __inline ALboolean IsReverbEffect(ALenum type)
|
||||
inline ALboolean IsReverbEffect(ALenum type)
|
||||
{ return type == AL_EFFECT_REVERB || type == AL_EFFECT_EAXREVERB; }
|
||||
|
||||
ALenum InitEffect(ALeffect *effect);
|
||||
ALvoid ReleaseALEffects(ALCdevice *device);
|
||||
void InitEffect(ALeffect *effect);
|
||||
void ReleaseALEffects(ALCdevice *device);
|
||||
|
||||
ALvoid LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef _AL_ERROR_H_
|
||||
#define _AL_ERROR_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "alMain.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -10,7 +10,17 @@ extern "C" {
|
||||
|
||||
extern ALboolean TrapALError;
|
||||
|
||||
ALvoid alSetError(ALCcontext *Context, ALenum errorCode);
|
||||
void alSetError(ALCcontext *context, ALenum errorCode, const char *msg, ...) DECL_FORMAT(printf, 3, 4);
|
||||
|
||||
#define SETERR_GOTO(ctx, err, lbl, ...) do { \
|
||||
alSetError((ctx), (err), __VA_ARGS__); \
|
||||
goto lbl; \
|
||||
} while(0)
|
||||
|
||||
#define SETERR_RETURN(ctx, err, retval, ...) do { \
|
||||
alSetError((ctx), (err), __VA_ARGS__); \
|
||||
return retval; \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+37
-80
@@ -1,74 +1,38 @@
|
||||
#ifndef _AL_FILTER_H_
|
||||
#define _AL_FILTER_H_
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/al.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ALfloat coeff;
|
||||
#ifndef _MSC_VER
|
||||
ALfloat history[0];
|
||||
#else
|
||||
ALfloat history[1];
|
||||
#endif
|
||||
} FILTER;
|
||||
#define LOWPASSFREQREF (5000.0f)
|
||||
#define HIGHPASSFREQREF (250.0f)
|
||||
|
||||
static __inline ALfloat lpFilter2P(FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
ALfloat *history = &iir->history[offset*2];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
output = output + (history[1]-output)*a;
|
||||
history[1] = output;
|
||||
struct ALfilter;
|
||||
|
||||
return output;
|
||||
typedef struct ALfilterVtable {
|
||||
void (*const setParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*const setParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*const setParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*const setParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
|
||||
void (*const getParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*const getParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*const getParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*const getParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
} ALfilterVtable;
|
||||
|
||||
#define DEFINE_ALFILTER_VTABLE(T) \
|
||||
const struct ALfilterVtable T##_vtable = { \
|
||||
T##_setParami, T##_setParamiv, \
|
||||
T##_setParamf, T##_setParamfv, \
|
||||
T##_getParami, T##_getParamiv, \
|
||||
T##_getParamf, T##_getParamfv, \
|
||||
}
|
||||
static __inline ALfloat lpFilter1P(FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
ALfloat *history = &iir->history[offset];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
history[0] = output;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static __inline ALfloat lpFilter2PC(const FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
const ALfloat *history = &iir->history[offset*2];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
output = output + (history[1]-output)*a;
|
||||
|
||||
return output;
|
||||
}
|
||||
static __inline ALfloat lpFilter1PC(FILTER *iir, ALuint offset, ALfloat input)
|
||||
{
|
||||
const ALfloat *history = &iir->history[offset];
|
||||
ALfloat a = iir->coeff;
|
||||
ALfloat output = input;
|
||||
|
||||
output = output + (history[0]-output)*a;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/* Calculates the low-pass filter coefficient given the pre-scaled gain and
|
||||
* cos(w) value. Note that g should be pre-scaled (sqr(gain) for one-pole,
|
||||
* sqrt(gain) for four-pole, etc) */
|
||||
ALfloat lpCoeffCalc(ALfloat g, ALfloat cw);
|
||||
|
||||
|
||||
typedef struct ALfilter {
|
||||
// Filter type (AL_FILTER_NULL, ...)
|
||||
@@ -76,32 +40,25 @@ typedef struct ALfilter {
|
||||
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
|
||||
void (*SetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint val);
|
||||
void (*SetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALint *vals);
|
||||
void (*SetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat val);
|
||||
void (*SetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, const ALfloat *vals);
|
||||
const struct ALfilterVtable *vtab;
|
||||
|
||||
void (*GetParami)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *val);
|
||||
void (*GetParamiv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALint *vals);
|
||||
void (*GetParamf)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *val);
|
||||
void (*GetParamfv)(struct ALfilter *filter, ALCcontext *context, ALenum param, ALfloat *vals);
|
||||
|
||||
// Index to itself
|
||||
ALuint filter;
|
||||
/* Self ID */
|
||||
ALuint id;
|
||||
} ALfilter;
|
||||
#define ALfilter_setParami(o, c, p, v) ((o)->vtab->setParami(o, c, p, v))
|
||||
#define ALfilter_setParamf(o, c, p, v) ((o)->vtab->setParamf(o, c, p, v))
|
||||
#define ALfilter_setParamiv(o, c, p, v) ((o)->vtab->setParamiv(o, c, p, v))
|
||||
#define ALfilter_setParamfv(o, c, p, v) ((o)->vtab->setParamfv(o, c, p, v))
|
||||
#define ALfilter_getParami(o, c, p, v) ((o)->vtab->getParami(o, c, p, v))
|
||||
#define ALfilter_getParamf(o, c, p, v) ((o)->vtab->getParamf(o, c, p, v))
|
||||
#define ALfilter_getParamiv(o, c, p, v) ((o)->vtab->getParamiv(o, c, p, v))
|
||||
#define ALfilter_getParamfv(o, c, p, v) ((o)->vtab->getParamfv(o, c, p, v))
|
||||
|
||||
#define ALfilter_SetParami(x, c, p, v) ((x)->SetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamiv(x, c, p, v) ((x)->SetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamf(x, c, p, v) ((x)->SetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_SetParamfv(x, c, p, v) ((x)->SetParamfv((x),(c),(p),(v)))
|
||||
|
||||
#define ALfilter_GetParami(x, c, p, v) ((x)->GetParami((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamiv(x, c, p, v) ((x)->GetParamiv((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamf(x, c, p, v) ((x)->GetParamf((x),(c),(p),(v)))
|
||||
#define ALfilter_GetParamfv(x, c, p, v) ((x)->GetParamfv((x),(c),(p),(v)))
|
||||
|
||||
ALvoid ReleaseALFilters(ALCdevice *device);
|
||||
void ReleaseALFilters(ALCdevice *device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,23 +1,65 @@
|
||||
#ifndef _AL_LISTENER_H_
|
||||
#define _AL_LISTENER_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ALlistener_struct
|
||||
{
|
||||
volatile ALfloat Position[3];
|
||||
volatile ALfloat Velocity[3];
|
||||
volatile ALfloat Forward[3];
|
||||
volatile ALfloat Up[3];
|
||||
volatile ALfloat Matrix[4][4];
|
||||
volatile ALfloat Gain;
|
||||
volatile ALfloat MetersPerUnit;
|
||||
struct ALcontextProps {
|
||||
ALfloat DopplerFactor;
|
||||
ALfloat DopplerVelocity;
|
||||
ALfloat SpeedOfSound;
|
||||
ALboolean SourceDistanceModel;
|
||||
enum DistanceModel DistanceModel;
|
||||
ALfloat MetersPerUnit;
|
||||
|
||||
ATOMIC(struct ALcontextProps*) next;
|
||||
};
|
||||
|
||||
struct ALlistenerProps {
|
||||
ALfloat Position[3];
|
||||
ALfloat Velocity[3];
|
||||
ALfloat Forward[3];
|
||||
ALfloat Up[3];
|
||||
ALfloat Gain;
|
||||
|
||||
ATOMIC(struct ALlistenerProps*) next;
|
||||
};
|
||||
|
||||
typedef struct ALlistener {
|
||||
alignas(16) ALfloat Position[3];
|
||||
ALfloat Velocity[3];
|
||||
ALfloat Forward[3];
|
||||
ALfloat Up[3];
|
||||
ALfloat Gain;
|
||||
|
||||
ATOMIC_FLAG PropsClean;
|
||||
|
||||
/* Pointer to the most recent property values that are awaiting an update.
|
||||
*/
|
||||
ATOMIC(struct ALlistenerProps*) Update;
|
||||
|
||||
struct {
|
||||
aluMatrixf Matrix;
|
||||
aluVector Velocity;
|
||||
|
||||
ALfloat Gain;
|
||||
ALfloat MetersPerUnit;
|
||||
|
||||
ALfloat DopplerFactor;
|
||||
ALfloat SpeedOfSound; /* in units per sec! */
|
||||
ALfloat ReverbSpeedOfSound; /* in meters per sec! */
|
||||
|
||||
ALboolean SourceDistanceModel;
|
||||
enum DistanceModel DistanceModel;
|
||||
} Params;
|
||||
} ALlistener;
|
||||
|
||||
void UpdateListenerProps(ALCcontext *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+759
-606
File diff suppressed because it is too large
Load Diff
+82
-104
@@ -1,137 +1,115 @@
|
||||
#ifndef _AL_SOURCE_H_
|
||||
#define _AL_SOURCE_H_
|
||||
|
||||
#define MAX_SENDS 4
|
||||
|
||||
#include "alFilter.h"
|
||||
#include "bool.h"
|
||||
#include "alMain.h"
|
||||
#include "alu.h"
|
||||
#include "AL/al.h"
|
||||
#include "hrtf.h"
|
||||
#include "atomic.h"
|
||||
|
||||
#define MAX_SENDS 16
|
||||
#define DEFAULT_SENDS 2
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SRC_HISTORY_BITS (6)
|
||||
#define SRC_HISTORY_LENGTH (1<<SRC_HISTORY_BITS)
|
||||
#define SRC_HISTORY_MASK (SRC_HISTORY_LENGTH-1)
|
||||
|
||||
extern enum Resampler DefaultResampler;
|
||||
|
||||
extern const ALsizei ResamplerPadding[ResamplerMax];
|
||||
extern const ALsizei ResamplerPrePadding[ResamplerMax];
|
||||
struct ALbuffer;
|
||||
struct ALsource;
|
||||
|
||||
|
||||
typedef struct ALbufferlistitem
|
||||
{
|
||||
struct ALbuffer *buffer;
|
||||
struct ALbufferlistitem *next;
|
||||
struct ALbufferlistitem *prev;
|
||||
typedef struct ALbufferlistitem {
|
||||
ATOMIC(struct ALbufferlistitem*) next;
|
||||
ALsizei max_samples;
|
||||
ALsizei num_buffers;
|
||||
struct ALbuffer *buffers[];
|
||||
} ALbufferlistitem;
|
||||
|
||||
typedef struct ALsource
|
||||
{
|
||||
volatile ALfloat flPitch;
|
||||
volatile ALfloat flGain;
|
||||
volatile ALfloat flOuterGain;
|
||||
volatile ALfloat flMinGain;
|
||||
volatile ALfloat flMaxGain;
|
||||
volatile ALfloat flInnerAngle;
|
||||
volatile ALfloat flOuterAngle;
|
||||
volatile ALfloat flRefDistance;
|
||||
volatile ALfloat flMaxDistance;
|
||||
volatile ALfloat flRollOffFactor;
|
||||
volatile ALfloat vPosition[3];
|
||||
volatile ALfloat vVelocity[3];
|
||||
volatile ALfloat vOrientation[3];
|
||||
volatile ALboolean bHeadRelative;
|
||||
volatile ALboolean bLooping;
|
||||
volatile enum DistanceModel DistanceModel;
|
||||
volatile ALboolean DirectChannels;
|
||||
|
||||
typedef struct ALsource {
|
||||
/** Source properties. */
|
||||
ALfloat Pitch;
|
||||
ALfloat Gain;
|
||||
ALfloat OuterGain;
|
||||
ALfloat MinGain;
|
||||
ALfloat MaxGain;
|
||||
ALfloat InnerAngle;
|
||||
ALfloat OuterAngle;
|
||||
ALfloat RefDistance;
|
||||
ALfloat MaxDistance;
|
||||
ALfloat RolloffFactor;
|
||||
ALfloat Position[3];
|
||||
ALfloat Velocity[3];
|
||||
ALfloat Direction[3];
|
||||
ALfloat Orientation[2][3];
|
||||
ALboolean HeadRelative;
|
||||
ALboolean Looping;
|
||||
enum DistanceModel DistanceModel;
|
||||
enum Resampler Resampler;
|
||||
ALboolean DirectChannels;
|
||||
enum SpatializeMode Spatialize;
|
||||
|
||||
volatile ALenum state;
|
||||
ALenum new_state;
|
||||
ALuint position;
|
||||
ALuint position_fraction;
|
||||
ALboolean DryGainHFAuto;
|
||||
ALboolean WetGainAuto;
|
||||
ALboolean WetGainHFAuto;
|
||||
ALfloat OuterGainHF;
|
||||
|
||||
ALbufferlistitem *queue; // Linked list of buffers in queue
|
||||
ALuint BuffersInQueue; // Number of buffers in queue
|
||||
ALuint BuffersPlayed; // Number of buffers played on this loop
|
||||
ALfloat AirAbsorptionFactor;
|
||||
ALfloat RoomRolloffFactor;
|
||||
ALfloat DopplerFactor;
|
||||
|
||||
ALfloat DirectGain;
|
||||
ALfloat DirectGainHF;
|
||||
/* NOTE: Stereo pan angles are specified in radians, counter-clockwise
|
||||
* rather than clockwise.
|
||||
*/
|
||||
ALfloat StereoPan[2];
|
||||
|
||||
ALfloat Radius;
|
||||
|
||||
/** Direct filter and auxiliary send info. */
|
||||
struct {
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
} Direct;
|
||||
struct {
|
||||
struct ALeffectslot *Slot;
|
||||
ALfloat WetGain;
|
||||
ALfloat WetGainHF;
|
||||
} Send[MAX_SENDS];
|
||||
ALfloat Gain;
|
||||
ALfloat GainHF;
|
||||
ALfloat HFReference;
|
||||
ALfloat GainLF;
|
||||
ALfloat LFReference;
|
||||
} *Send;
|
||||
|
||||
volatile ALboolean DryGainHFAuto;
|
||||
volatile ALboolean WetGainAuto;
|
||||
volatile ALboolean WetGainHFAuto;
|
||||
volatile ALfloat OuterGainHF;
|
||||
/**
|
||||
* Last user-specified offset, and the offset type (bytes, samples, or
|
||||
* seconds).
|
||||
*/
|
||||
ALdouble Offset;
|
||||
ALenum OffsetType;
|
||||
|
||||
volatile ALfloat AirAbsorptionFactor;
|
||||
volatile ALfloat RoomRolloffFactor;
|
||||
volatile ALfloat DopplerFactor;
|
||||
/** Source type (static, streaming, or undetermined) */
|
||||
ALint SourceType;
|
||||
|
||||
ALint lOffset;
|
||||
ALint lOffsetType;
|
||||
/** Source state (initial, playing, paused, or stopped) */
|
||||
ALenum state;
|
||||
|
||||
// Source Type (Static, Streaming, or Undetermined)
|
||||
volatile ALint lSourceType;
|
||||
/** Source Buffer Queue head. */
|
||||
ALbufferlistitem *queue;
|
||||
|
||||
ALuint NumChannels;
|
||||
ALuint SampleSize;
|
||||
ATOMIC_FLAG PropsClean;
|
||||
|
||||
/* HRTF info */
|
||||
ALboolean HrtfMoving;
|
||||
ALuint HrtfCounter;
|
||||
ALfloat HrtfHistory[MAXCHANNELS][SRC_HISTORY_LENGTH];
|
||||
ALfloat HrtfValues[MAXCHANNELS][HRIR_LENGTH][2];
|
||||
ALuint HrtfOffset;
|
||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||
* reset when looking up the voice.
|
||||
*/
|
||||
ALint VoiceIdx;
|
||||
|
||||
/* Current target parameters used for mixing */
|
||||
struct {
|
||||
MixerFunc DoMix;
|
||||
|
||||
ALint Step;
|
||||
|
||||
ALfloat HrtfGain;
|
||||
ALfloat HrtfDir[3];
|
||||
ALfloat HrtfCoeffs[MAXCHANNELS][HRIR_LENGTH][2];
|
||||
ALuint HrtfDelay[MAXCHANNELS][2];
|
||||
ALfloat HrtfCoeffStep[HRIR_LENGTH][2];
|
||||
ALint HrtfDelayStep[2];
|
||||
|
||||
/* A mixing matrix. First subscript is the channel number of the input
|
||||
* data (regardless of channel configuration) and the second is the
|
||||
* channel target (eg. FRONT_LEFT) */
|
||||
ALfloat DryGains[MAXCHANNELS][MAXCHANNELS];
|
||||
|
||||
FILTER iirFilter;
|
||||
ALfloat history[MAXCHANNELS*2];
|
||||
|
||||
struct {
|
||||
struct ALeffectslot *Slot;
|
||||
ALfloat WetGain;
|
||||
FILTER iirFilter;
|
||||
ALfloat history[MAXCHANNELS];
|
||||
} Send[MAX_SENDS];
|
||||
} Params;
|
||||
volatile ALenum NeedsUpdate;
|
||||
|
||||
ALvoid (*Update)(struct ALsource *self, const ALCcontext *context);
|
||||
|
||||
// Index to itself
|
||||
ALuint source;
|
||||
/** Self ID */
|
||||
ALuint id;
|
||||
} ALsource;
|
||||
#define ALsource_Update(s,a) ((s)->Update(s,a))
|
||||
|
||||
ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state);
|
||||
ALboolean ApplyOffset(ALsource *Source);
|
||||
void UpdateAllSourceProps(ALCcontext *context);
|
||||
|
||||
ALvoid ReleaseALSources(ALCcontext *Context);
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef _AL_STATE_H_
|
||||
#define _AL_STATE_H_
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef ALTHUNK_H
|
||||
#define ALTHUNK_H
|
||||
|
||||
#include "alMain.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void ThunkInit(void);
|
||||
void ThunkExit(void);
|
||||
ALenum NewThunkEntry(ALuint *index);
|
||||
void FreeThunkEntry(ALuint index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //ALTHUNK_H
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user